Skip to content

Commit 269718f

Browse files
Enhance Tests around SnapshotInfo UserMetadata (#74362)
We barely test the correct handling of user metadata directly. With upcoming changes to how `SnapshotInfo` is stored it would be nice to have better test coverage. This PR adds randomized coverage of serializing user metadata to a large number of tests that all user the shared infrastructure that is adjusted here.
1 parent f5e0536 commit 269718f

File tree

4 files changed

+106
-54
lines changed

4 files changed

+106
-54
lines changed

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

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@
3333
import org.elasticsearch.common.xcontent.XContentType;
3434
import org.elasticsearch.repositories.fs.FsRepository;
3535
import org.elasticsearch.rest.RestStatus;
36+
import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase;
3637
import org.elasticsearch.snapshots.RestoreInfo;
3738
import org.mockito.internal.util.collections.Sets;
3839

3940
import java.io.IOException;
4041
import java.util.Collections;
41-
import java.util.HashMap;
4242
import java.util.List;
4343
import java.util.Map;
4444

@@ -149,7 +149,7 @@ public void testCreateSnapshot() throws Exception {
149149
boolean waitForCompletion = randomBoolean();
150150
request.waitForCompletion(waitForCompletion);
151151
if (randomBoolean()) {
152-
request.userMetadata(randomUserMetadata());
152+
request.userMetadata(AbstractSnapshotIntegTestCase.randomUserMetadata());
153153
}
154154
request.partial(randomBoolean());
155155
request.includeGlobalState(randomBoolean());
@@ -193,7 +193,7 @@ public void testGetSnapshots() throws IOException {
193193
CreateSnapshotResponse putSnapshotResponse1 = createTestSnapshot(createSnapshotRequest1);
194194
CreateSnapshotRequest createSnapshotRequest2 = new CreateSnapshotRequest(repository2, snapshot2);
195195
createSnapshotRequest2.waitForCompletion(true);
196-
Map<String, Object> originalMetadata = randomUserMetadata();
196+
Map<String, Object> originalMetadata = AbstractSnapshotIntegTestCase.randomUserMetadata();
197197
createSnapshotRequest2.userMetadata(originalMetadata);
198198
CreateSnapshotResponse putSnapshotResponse2 = createTestSnapshot(createSnapshotRequest2);
199199
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
@@ -264,7 +264,7 @@ public void testRestoreSnapshot() throws IOException {
264264
createSnapshotRequest.indices(testIndex);
265265
createSnapshotRequest.waitForCompletion(true);
266266
if (randomBoolean()) {
267-
createSnapshotRequest.userMetadata(randomUserMetadata());
267+
createSnapshotRequest.userMetadata(AbstractSnapshotIntegTestCase.randomUserMetadata());
268268
}
269269
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
270270
assertEquals(RestStatus.OK, createSnapshotResponse.status());
@@ -311,7 +311,7 @@ public void testSnapshotHidden() throws IOException {
311311
createSnapshotRequest.indices("*");
312312
createSnapshotRequest.waitForCompletion(true);
313313
if (randomBoolean()) {
314-
createSnapshotRequest.userMetadata(randomUserMetadata());
314+
createSnapshotRequest.userMetadata(AbstractSnapshotIntegTestCase.randomUserMetadata());
315315
}
316316
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
317317
assertEquals(RestStatus.OK, createSnapshotResponse.status());
@@ -344,7 +344,7 @@ public void testDeleteSnapshot() throws IOException {
344344
CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(repository, snapshot);
345345
createSnapshotRequest.waitForCompletion(true);
346346
if (randomBoolean()) {
347-
createSnapshotRequest.userMetadata(randomUserMetadata());
347+
createSnapshotRequest.userMetadata(AbstractSnapshotIntegTestCase.randomUserMetadata());
348348
}
349349
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
350350
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
@@ -380,27 +380,4 @@ public void testCloneSnapshot() throws IOException {
380380
assertTrue(response.isAcknowledged());
381381
}
382382

383-
private static Map<String, Object> randomUserMetadata() {
384-
if (randomBoolean()) {
385-
return null;
386-
}
387-
388-
Map<String, Object> metadata = new HashMap<>();
389-
long fields = randomLongBetween(0, 4);
390-
for (int i = 0; i < fields; i++) {
391-
if (randomBoolean()) {
392-
metadata.put(randomValueOtherThanMany(metadata::containsKey, () -> randomAlphaOfLengthBetween(2,10)),
393-
randomAlphaOfLengthBetween(5, 5));
394-
} else {
395-
Map<String, Object> nested = new HashMap<>();
396-
long nestedFields = randomLongBetween(0, 4);
397-
for (int j = 0; j < nestedFields; j++) {
398-
nested.put(randomValueOtherThanMany(nested::containsKey, () -> randomAlphaOfLengthBetween(2,10)),
399-
randomAlphaOfLengthBetween(5, 5));
400-
}
401-
metadata.put(randomValueOtherThanMany(metadata::containsKey, () -> randomAlphaOfLengthBetween(2,10)), nested);
402-
}
403-
}
404-
return metadata;
405-
}
406383
}

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.common.xcontent.ToXContentObject;
2323
import org.elasticsearch.common.xcontent.XContentBuilder;
2424
import org.elasticsearch.common.xcontent.XContentFactory;
25+
import org.elasticsearch.core.Nullable;
2526

2627
import java.io.IOException;
2728
import java.util.Arrays;
@@ -74,6 +75,7 @@ public class CreateSnapshotRequest extends MasterNodeRequest<CreateSnapshotReque
7475

7576
private boolean waitForCompletion;
7677

78+
@Nullable
7779
private Map<String, Object> userMetadata;
7880

7981
public CreateSnapshotRequest() {}
@@ -338,11 +340,19 @@ public boolean includeGlobalState() {
338340
return includeGlobalState;
339341
}
340342

343+
/**
344+
* @return user metadata map that should be stored with the snapshot or {@code null} if there is no user metadata to be associated with
345+
* this snapshot
346+
*/
347+
@Nullable
341348
public Map<String, Object> userMetadata() {
342349
return userMetadata;
343350
}
344351

345-
public CreateSnapshotRequest userMetadata(Map<String, Object> userMetadata) {
352+
/**
353+
* @param userMetadata user metadata map that should be stored with the snapshot
354+
*/
355+
public CreateSnapshotRequest userMetadata(@Nullable Map<String, Object> userMetadata) {
346356
this.userMetadata = userMetadata;
347357
return this;
348358
}
@@ -379,29 +389,35 @@ public CreateSnapshotRequest featureStates(List<String> featureStates) {
379389
public CreateSnapshotRequest source(Map<String, Object> source) {
380390
for (Map.Entry<String, Object> entry : source.entrySet()) {
381391
String name = entry.getKey();
382-
if (name.equals("indices")) {
383-
if (entry.getValue() instanceof String) {
384-
indices(Strings.splitStringByCommaToArray((String) entry.getValue()));
385-
} else if (entry.getValue() instanceof List) {
386-
indices((List<String>) entry.getValue());
387-
} else {
388-
throw new IllegalArgumentException("malformed indices section, should be an array of strings");
389-
}
390-
} else if (name.equals("feature_states")) {
391-
if (entry.getValue() instanceof List) {
392-
featureStates((List<String>) entry.getValue());
393-
} else {
394-
throw new IllegalArgumentException("malformed feature_states section, should be an array of strings");
395-
}
396-
} else if (name.equals("partial")) {
397-
partial(nodeBooleanValue(entry.getValue(), "partial"));
398-
} else if (name.equals("include_global_state")) {
399-
includeGlobalState = nodeBooleanValue(entry.getValue(), "include_global_state");
400-
} else if (name.equals("metadata")) {
401-
if (entry.getValue() != null && (entry.getValue() instanceof Map == false)) {
402-
throw new IllegalArgumentException("malformed metadata, should be an object");
403-
}
404-
userMetadata((Map<String, Object>) entry.getValue());
392+
switch (name) {
393+
case "indices":
394+
if (entry.getValue() instanceof String) {
395+
indices(Strings.splitStringByCommaToArray((String) entry.getValue()));
396+
} else if (entry.getValue() instanceof List) {
397+
indices((List<String>) entry.getValue());
398+
} else {
399+
throw new IllegalArgumentException("malformed indices section, should be an array of strings");
400+
}
401+
break;
402+
case "feature_states":
403+
if (entry.getValue() instanceof List) {
404+
featureStates((List<String>) entry.getValue());
405+
} else {
406+
throw new IllegalArgumentException("malformed feature_states section, should be an array of strings");
407+
}
408+
break;
409+
case "partial":
410+
partial(nodeBooleanValue(entry.getValue(), "partial"));
411+
break;
412+
case "include_global_state":
413+
includeGlobalState = nodeBooleanValue(entry.getValue(), "include_global_state");
414+
break;
415+
case "metadata":
416+
if (entry.getValue() != null && (entry.getValue() instanceof Map == false)) {
417+
throw new IllegalArgumentException("malformed metadata, should be an object");
418+
}
419+
userMetadata((Map<String, Object>) entry.getValue());
420+
break;
405421
}
406422
}
407423
indicesOptions(IndicesOptions.fromMap(source, indicesOptions));

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestBuilder.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import org.elasticsearch.action.support.IndicesOptions;
1212
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
1313
import org.elasticsearch.client.ElasticsearchClient;
14+
import org.elasticsearch.core.Nullable;
15+
16+
import java.util.Map;
1417

1518
/**
1619
* Create snapshot request builder
@@ -124,4 +127,15 @@ public CreateSnapshotRequestBuilder setFeatureStates(String... featureStates) {
124127
request.featureStates(featureStates);
125128
return this;
126129
}
130+
131+
/**
132+
* Provide a map of user metadata that should be included in the snapshot metadata.
133+
*
134+
* @param metadata user metadata map
135+
* @return this builder
136+
*/
137+
public CreateSnapshotRequestBuilder setUserMetadata(@Nullable Map<String, Object> metadata) {
138+
request.userMetadata(metadata);
139+
return this;
140+
}
127141
}

test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import java.util.Arrays;
7373
import java.util.Collection;
7474
import java.util.Collections;
75+
import java.util.HashMap;
7576
import java.util.List;
7677
import java.util.Locale;
7778
import java.util.Map;
@@ -654,7 +655,21 @@ public static List<String> createNSnapshots(Logger logger, String repoName, int
654655
for (int i = 0; i < count; i++) {
655656
final String snapshot = prefix + i;
656657
snapshotNames.add(snapshot);
657-
client().admin().cluster().prepareCreateSnapshot(repoName, snapshot).setWaitForCompletion(true).execute(snapshotsListener);
658+
final Map<String, Object> userMetadata = randomUserMetadata();
659+
clusterAdmin()
660+
.prepareCreateSnapshot(repoName, snapshot)
661+
.setWaitForCompletion(true)
662+
.setUserMetadata(userMetadata)
663+
.execute(snapshotsListener.delegateFailure((l, response) -> {
664+
final SnapshotInfo snapshotInfoInResponse = response.getSnapshotInfo();
665+
assertEquals(userMetadata, snapshotInfoInResponse.userMetadata());
666+
clusterAdmin().prepareGetSnapshots(repoName)
667+
.setSnapshots(snapshot)
668+
.execute(l.delegateFailure((ll, getResponse) -> {
669+
assertEquals(snapshotInfoInResponse, getResponse.getSnapshots(repoName).get(0));
670+
ll.onResponse(response);
671+
}));
672+
}));
658673
}
659674
for (CreateSnapshotResponse snapshotResponse : allSnapshotsDone.get()) {
660675
assertThat(snapshotResponse.getSnapshotInfo().state(), is(SnapshotState.SUCCESS));
@@ -708,4 +723,34 @@ public static void assertSnapshotListSorted(List<SnapshotInfo> snapshotInfos, @N
708723
orderAssertion.accept(snapshotInfos.get(i), snapshotInfos.get(i + 1));
709724
}
710725
}
726+
727+
/**
728+
* Randomly either generates some random snapshot user metadata or returns {@code null}.
729+
*
730+
* @return random snapshot user metadata or {@code null}
731+
*/
732+
@Nullable
733+
public static Map<String, Object> randomUserMetadata() {
734+
if (randomBoolean()) {
735+
return null;
736+
}
737+
738+
Map<String, Object> metadata = new HashMap<>();
739+
long fields = randomLongBetween(0, 4);
740+
for (int i = 0; i < fields; i++) {
741+
if (randomBoolean()) {
742+
metadata.put(randomValueOtherThanMany(metadata::containsKey, () -> randomAlphaOfLengthBetween(2, 10)),
743+
randomAlphaOfLengthBetween(5, 5));
744+
} else {
745+
Map<String, Object> nested = new HashMap<>();
746+
long nestedFields = randomLongBetween(0, 4);
747+
for (int j = 0; j < nestedFields; j++) {
748+
nested.put(randomValueOtherThanMany(nested::containsKey, () -> randomAlphaOfLengthBetween(2, 10)),
749+
randomAlphaOfLengthBetween(5, 5));
750+
}
751+
metadata.put(randomValueOtherThanMany(metadata::containsKey, () -> randomAlphaOfLengthBetween(2, 10)), nested);
752+
}
753+
}
754+
return metadata;
755+
}
711756
}

0 commit comments

Comments
 (0)