Skip to content

Commit 55a5a2b

Browse files
Add REST Test for Snapshot Clone API (elastic#63863) (elastic#63879)
Adds snapshot clone REST tests and HLRC support for the API.
1 parent 3a370dd commit 55a5a2b

File tree

6 files changed

+194
-1
lines changed

6 files changed

+194
-1
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
2929
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
3030
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
31+
import org.elasticsearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest;
3132
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
3233
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
3334
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@@ -236,6 +237,32 @@ public Cancellable createAsync(CreateSnapshotRequest createSnapshotRequest, Requ
236237
CreateSnapshotResponse::fromXContent, listener, emptySet());
237238
}
238239

240+
/**
241+
* Clones a snapshot.
242+
* <p>
243+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
244+
* API on elastic.co</a>
245+
*/
246+
public AcknowledgedResponse clone(CloneSnapshotRequest cloneSnapshotRequest, RequestOptions options)
247+
throws IOException {
248+
return restHighLevelClient.performRequestAndParseEntity(cloneSnapshotRequest, SnapshotRequestConverters::cloneSnapshot, options,
249+
AcknowledgedResponse::fromXContent, emptySet());
250+
}
251+
252+
/**
253+
* Asynchronously clones a snapshot.
254+
* <p>
255+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
256+
* API on elastic.co</a>
257+
* @return cancellable that may be used to cancel the request
258+
*/
259+
public Cancellable cloneAsync(CloneSnapshotRequest cloneSnapshotRequest, RequestOptions options,
260+
ActionListener<AcknowledgedResponse> listener) {
261+
return restHighLevelClient.performRequestAsyncAndParseEntity(cloneSnapshotRequest,
262+
SnapshotRequestConverters::cloneSnapshot, options,
263+
AcknowledgedResponse::fromXContent, listener, emptySet());
264+
}
265+
239266
/**
240267
* Get snapshots.
241268
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore

client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotRequestConverters.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest;
2929
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
3030
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
31+
import org.elasticsearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest;
3132
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
3233
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
3334
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
@@ -123,6 +124,21 @@ static Request createSnapshot(CreateSnapshotRequest createSnapshotRequest) throw
123124
return request;
124125
}
125126

127+
static Request cloneSnapshot(CloneSnapshotRequest cloneSnapshotRequest) throws IOException {
128+
String endpoint = new RequestConverters.EndpointBuilder().addPathPart("_snapshot")
129+
.addPathPart(cloneSnapshotRequest.repository())
130+
.addPathPart(cloneSnapshotRequest.source())
131+
.addPathPart("_clone")
132+
.addPathPart(cloneSnapshotRequest.target())
133+
.build();
134+
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
135+
RequestConverters.Params params = new RequestConverters.Params();
136+
params.withMasterTimeout(cloneSnapshotRequest.masterNodeTimeout());
137+
request.addParameters(params.asMap());
138+
request.setEntity(RequestConverters.createEntity(cloneSnapshotRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE));
139+
return request;
140+
}
141+
126142
static Request getSnapshots(GetSnapshotsRequest getSnapshotsRequest) {
127143
RequestConverters.EndpointBuilder endpointBuilder = new RequestConverters.EndpointBuilder().addPathPartAsIs("_snapshot")
128144
.addPathPart(getSnapshotsRequest.repository());

client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
2929
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
3030
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
31+
import org.elasticsearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest;
3132
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
3233
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
3334
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@@ -351,6 +352,30 @@ public void testDeleteSnapshot() throws IOException {
351352
assertTrue(response.isAcknowledged());
352353
}
353354

355+
public void testCloneSnapshot() throws IOException {
356+
String repository = "test_repository";
357+
String snapshot = "source_snapshot";
358+
String targetSnapshot = "target_snapshot";
359+
final String testIndex = "test_idx";
360+
361+
createIndex(testIndex, Settings.EMPTY);
362+
assertTrue("index [" + testIndex + "] should have been created", indexExists(testIndex));
363+
364+
AcknowledgedResponse putRepositoryResponse = createTestRepository(repository, FsRepository.TYPE, "{\"location\": \".\"}");
365+
assertTrue(putRepositoryResponse.isAcknowledged());
366+
367+
CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(repository, snapshot);
368+
createSnapshotRequest.waitForCompletion(true);
369+
370+
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
371+
assertEquals(RestStatus.OK, createSnapshotResponse.status());
372+
373+
CloneSnapshotRequest request = new CloneSnapshotRequest(repository, snapshot, targetSnapshot, new String[]{testIndex});
374+
AcknowledgedResponse response = execute(request, highLevelClient().snapshot()::clone, highLevelClient().snapshot()::cloneAsync);
375+
376+
assertTrue(response.isAcknowledged());
377+
}
378+
354379
private static Map<String, Object> randomUserMetadata() {
355380
if (randomBoolean()) {
356381
return null;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"snapshot.clone":{
3+
"documentation":{
4+
"url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-snapshots.html",
5+
"description":"Clones indices from one snapshot into another snapshot in the same repository."
6+
},
7+
"stability":"stable",
8+
"url":{
9+
"paths":[
10+
{
11+
"path":"/_snapshot/{repository}/{snapshot}/_clone/{target_snapshot}",
12+
"methods":[
13+
"PUT"
14+
],
15+
"parts":{
16+
"repository":{
17+
"type":"string",
18+
"description":"A repository name"
19+
},
20+
"snapshot":{
21+
"type":"string",
22+
"description":"The name of the snapshot to clone from"
23+
},
24+
"target_snapshot":{
25+
"type":"string",
26+
"description":"The name of the cloned snapshot to create"
27+
}
28+
}
29+
}
30+
]
31+
},
32+
"params":{
33+
"master_timeout":{
34+
"type":"time",
35+
"description":"Explicit operation timeout for connection to master node"
36+
}
37+
},
38+
"body":{
39+
"description":"The snapshot clone definition",
40+
"required":true
41+
}
42+
}
43+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
setup:
3+
4+
- do:
5+
snapshot.create_repository:
6+
repository: test_repo_create_1
7+
body:
8+
type: fs
9+
settings:
10+
location: "test_repo_create_1_loc"
11+
12+
- do:
13+
indices.create:
14+
index: test_index_1
15+
body:
16+
settings:
17+
number_of_shards: 1
18+
number_of_replicas: 1
19+
20+
- do:
21+
indices.create:
22+
index: test_index_2
23+
body:
24+
settings:
25+
number_of_shards: 1
26+
number_of_replicas: 1
27+
28+
- do:
29+
snapshot.create:
30+
repository: test_repo_create_1
31+
snapshot: test_snapshot
32+
wait_for_completion: true
33+
34+
---
35+
"Clone a snapshot":
36+
- skip:
37+
version: " - 7.9.99"
38+
reason: "Clone snapshot functionality was introduced in 7.10"
39+
- do:
40+
snapshot.clone:
41+
repository: test_repo_create_1
42+
snapshot: test_snapshot
43+
target_snapshot: target_snapshot_1
44+
body:
45+
"indices": test_index_2
46+
47+
- match: { acknowledged: true }
48+
49+
- do:
50+
snapshot.delete:
51+
repository: test_repo_create_1
52+
snapshot: target_snapshot_1
53+
54+
- match: { acknowledged: true }

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@
2323
import org.elasticsearch.action.IndicesRequest;
2424
import org.elasticsearch.action.support.IndicesOptions;
2525
import org.elasticsearch.action.support.master.MasterNodeRequest;
26+
import org.elasticsearch.common.Strings;
2627
import org.elasticsearch.common.io.stream.StreamInput;
2728
import org.elasticsearch.common.io.stream.StreamOutput;
29+
import org.elasticsearch.common.xcontent.ToXContentObject;
30+
import org.elasticsearch.common.xcontent.XContentBuilder;
2831

2932
import java.io.IOException;
3033

3134
import static org.elasticsearch.action.ValidateActions.addValidationError;
3235

33-
public class CloneSnapshotRequest extends MasterNodeRequest<CloneSnapshotRequest> implements IndicesRequest.Replaceable{
36+
public class CloneSnapshotRequest extends MasterNodeRequest<CloneSnapshotRequest> implements IndicesRequest.Replaceable, ToXContentObject {
3437

3538
private final String repository;
3639

@@ -139,4 +142,29 @@ public String target() {
139142
public String source() {
140143
return this.source;
141144
}
145+
146+
@Override
147+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
148+
builder.startObject();
149+
builder.field("repository", repository);
150+
builder.field("source", source);
151+
builder.field("target", target);
152+
if (indices != null) {
153+
builder.startArray("indices");
154+
for (String index : indices) {
155+
builder.value(index);
156+
}
157+
builder.endArray();
158+
}
159+
if (indicesOptions != null) {
160+
indicesOptions.toXContent(builder, params);
161+
}
162+
builder.endObject();
163+
return builder;
164+
}
165+
166+
@Override
167+
public String toString() {
168+
return Strings.toString(this);
169+
}
142170
}

0 commit comments

Comments
 (0)