|
33 | 33 | package org.opensearch.snapshots;
|
34 | 34 |
|
35 | 35 | import org.opensearch.Version;
|
| 36 | +import org.opensearch.action.ActionRequestValidationException; |
| 37 | +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; |
36 | 38 | import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
|
37 | 39 | import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
|
38 | 40 | import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse;
|
39 | 41 | import org.opensearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStage;
|
40 | 42 | import org.opensearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStatus;
|
| 43 | +import org.opensearch.action.admin.cluster.snapshots.status.SnapshotIndexStatus; |
41 | 44 | import org.opensearch.action.admin.cluster.snapshots.status.SnapshotStats;
|
42 | 45 | import org.opensearch.action.admin.cluster.snapshots.status.SnapshotStatus;
|
43 | 46 | import org.opensearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
|
|
49 | 52 | import org.opensearch.common.util.io.IOUtils;
|
50 | 53 | import org.opensearch.core.common.Strings;
|
51 | 54 | import org.opensearch.core.common.unit.ByteSizeUnit;
|
| 55 | +import org.opensearch.core.rest.RestStatus; |
52 | 56 | import org.opensearch.repositories.blobstore.BlobStoreRepository;
|
53 | 57 | import org.opensearch.threadpool.ThreadPool;
|
54 | 58 |
|
|
59 | 63 | import java.util.Collections;
|
60 | 64 | import java.util.List;
|
61 | 65 | import java.util.Locale;
|
| 66 | +import java.util.Map; |
| 67 | +import java.util.Set; |
62 | 68 | import java.util.concurrent.TimeUnit;
|
63 | 69 | import java.util.stream.Collectors;
|
64 | 70 |
|
| 71 | +import static org.opensearch.snapshots.SnapshotsService.MAX_SHARDS_ALLOWED_IN_STATUS_API; |
65 | 72 | import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked;
|
66 | 73 | import static org.hamcrest.Matchers.equalTo;
|
67 | 74 | import static org.hamcrest.Matchers.greaterThan;
|
@@ -564,6 +571,192 @@ public void testGetSnapshotsRequest() throws Exception {
|
564 | 571 | waitForCompletion(repositoryName, inProgressSnapshot, TimeValue.timeValueSeconds(60));
|
565 | 572 | }
|
566 | 573 |
|
| 574 | + public void testSnapshotStatusApiFailureForTooManyShardsAcrossSnapshots() throws Exception { |
| 575 | + String repositoryName = "test-repo"; |
| 576 | + String index1 = "test-idx-1"; |
| 577 | + String index2 = "test-idx-2"; |
| 578 | + String index3 = "test-idx-3"; |
| 579 | + createRepository(repositoryName, "fs"); |
| 580 | + |
| 581 | + logger.info("Create indices"); |
| 582 | + createIndex(index1, index2, index3); |
| 583 | + ensureGreen(); |
| 584 | + |
| 585 | + logger.info("Indexing some data"); |
| 586 | + for (int i = 0; i < 10; i++) { |
| 587 | + index(index1, "_doc", Integer.toString(i), "foo", "bar" + i); |
| 588 | + index(index2, "_doc", Integer.toString(i), "foo", "baz" + i); |
| 589 | + index(index3, "_doc", Integer.toString(i), "foo", "baz" + i); |
| 590 | + } |
| 591 | + refresh(); |
| 592 | + String snapshot1 = "test-snap-1"; |
| 593 | + String snapshot2 = "test-snap-2"; |
| 594 | + createSnapshot(repositoryName, snapshot1, List.of(index1, index2, index3)); |
| 595 | + createSnapshot(repositoryName, snapshot2, List.of(index1, index2)); |
| 596 | + |
| 597 | + logger.info("Set MAX_SHARDS_ALLOWED_IN_STATUS_API to a low value"); |
| 598 | + ClusterUpdateSettingsRequest updateSettingsRequest = new ClusterUpdateSettingsRequest(); |
| 599 | + updateSettingsRequest.persistentSettings(Settings.builder().put(MAX_SHARDS_ALLOWED_IN_STATUS_API.getKey(), 2)); |
| 600 | + assertAcked(client().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); |
| 601 | + |
| 602 | + // across a single snapshot |
| 603 | + assertBusy(() -> { |
| 604 | + SnapshotException snapshotException = expectThrows( |
| 605 | + SnapshotException.class, |
| 606 | + () -> client().admin().cluster().prepareSnapshotStatus(repositoryName).setSnapshots(snapshot1).execute().actionGet() |
| 607 | + ); |
| 608 | + assertEquals(snapshotException.status(), RestStatus.REQUEST_ENTITY_TOO_LARGE); |
| 609 | + assertTrue( |
| 610 | + snapshotException.getMessage() |
| 611 | + .endsWith(" is more than the maximum allowed value of shard count [2] for snapshot status request") |
| 612 | + ); |
| 613 | + }, 1, TimeUnit.MINUTES); |
| 614 | + |
| 615 | + // across multiple snapshots |
| 616 | + assertBusy(() -> { |
| 617 | + SnapshotException snapshotException = expectThrows( |
| 618 | + SnapshotException.class, |
| 619 | + () -> client().admin() |
| 620 | + .cluster() |
| 621 | + .prepareSnapshotStatus(repositoryName) |
| 622 | + .setSnapshots(snapshot1, snapshot2) |
| 623 | + .execute() |
| 624 | + .actionGet() |
| 625 | + ); |
| 626 | + assertEquals(snapshotException.status(), RestStatus.REQUEST_ENTITY_TOO_LARGE); |
| 627 | + assertTrue( |
| 628 | + snapshotException.getMessage() |
| 629 | + .endsWith(" is more than the maximum allowed value of shard count [2] for snapshot status request") |
| 630 | + ); |
| 631 | + }, 1, TimeUnit.MINUTES); |
| 632 | + |
| 633 | + logger.info("Reset MAX_SHARDS_ALLOWED_IN_STATUS_API to default value"); |
| 634 | + updateSettingsRequest.persistentSettings(Settings.builder().putNull(MAX_SHARDS_ALLOWED_IN_STATUS_API.getKey())); |
| 635 | + assertAcked(internalCluster().client().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); |
| 636 | + } |
| 637 | + |
| 638 | + public void testSnapshotStatusForIndexFilter() throws Exception { |
| 639 | + String repositoryName = "test-repo"; |
| 640 | + String index1 = "test-idx-1"; |
| 641 | + String index2 = "test-idx-2"; |
| 642 | + String index3 = "test-idx-3"; |
| 643 | + createRepository(repositoryName, "fs"); |
| 644 | + |
| 645 | + logger.info("Create indices"); |
| 646 | + createIndex(index1, index2, index3); |
| 647 | + ensureGreen(); |
| 648 | + |
| 649 | + logger.info("Indexing some data"); |
| 650 | + for (int i = 0; i < 10; i++) { |
| 651 | + index(index1, "_doc", Integer.toString(i), "foo", "bar" + i); |
| 652 | + index(index2, "_doc", Integer.toString(i), "foo", "baz" + i); |
| 653 | + index(index3, "_doc", Integer.toString(i), "foo", "baz" + i); |
| 654 | + } |
| 655 | + refresh(); |
| 656 | + String snapshot = "test-snap-1"; |
| 657 | + createSnapshot(repositoryName, snapshot, List.of(index1, index2, index3)); |
| 658 | + |
| 659 | + assertBusy(() -> { |
| 660 | + SnapshotStatus snapshotsStatus = client().admin() |
| 661 | + .cluster() |
| 662 | + .prepareSnapshotStatus(repositoryName) |
| 663 | + .setSnapshots(snapshot) |
| 664 | + .setIndices(index1, index2) |
| 665 | + .get() |
| 666 | + .getSnapshots() |
| 667 | + .get(0); |
| 668 | + Map<String, SnapshotIndexStatus> snapshotIndexStatusMap = snapshotsStatus.getIndices(); |
| 669 | + // Although the snapshot contains 3 indices, the response of status api call only contains results for 2 |
| 670 | + assertEquals(snapshotIndexStatusMap.size(), 2); |
| 671 | + assertEquals(snapshotIndexStatusMap.keySet(), Set.of(index1, index2)); |
| 672 | + }, 1, TimeUnit.MINUTES); |
| 673 | + } |
| 674 | + |
| 675 | + public void testSnapshotStatusFailuresWithIndexFilter() throws Exception { |
| 676 | + String repositoryName = "test-repo"; |
| 677 | + String index1 = "test-idx-1"; |
| 678 | + String index2 = "test-idx-2"; |
| 679 | + String index3 = "test-idx-3"; |
| 680 | + createRepository(repositoryName, "fs"); |
| 681 | + |
| 682 | + logger.info("Create indices"); |
| 683 | + createIndex(index1, index2, index3); |
| 684 | + ensureGreen(); |
| 685 | + |
| 686 | + logger.info("Indexing some data"); |
| 687 | + for (int i = 0; i < 10; i++) { |
| 688 | + index(index1, "_doc", Integer.toString(i), "foo", "bar" + i); |
| 689 | + index(index2, "_doc", Integer.toString(i), "foo", "baz" + i); |
| 690 | + index(index3, "_doc", Integer.toString(i), "foo", "baz" + i); |
| 691 | + } |
| 692 | + refresh(); |
| 693 | + String snapshot1 = "test-snap-1"; |
| 694 | + String snapshot2 = "test-snap-2"; |
| 695 | + createSnapshot(repositoryName, snapshot1, List.of(index1, index2, index3)); |
| 696 | + createSnapshot(repositoryName, snapshot2, List.of(index1)); |
| 697 | + |
| 698 | + assertBusy(() -> { |
| 699 | + // failure due to passing index filter for multiple snapshots |
| 700 | + ActionRequestValidationException ex1 = expectThrows( |
| 701 | + ActionRequestValidationException.class, |
| 702 | + () -> client().admin() |
| 703 | + .cluster() |
| 704 | + .prepareSnapshotStatus(repositoryName) |
| 705 | + .setSnapshots(snapshot1, snapshot2) |
| 706 | + .setIndices(index1, index2, index3) |
| 707 | + .execute() |
| 708 | + .actionGet() |
| 709 | + ); |
| 710 | + String cause = "index list filter is supported only for a single snapshot"; |
| 711 | + assertTrue(ex1.getMessage().contains(cause)); |
| 712 | + |
| 713 | + // failure due to index not found in snapshot |
| 714 | + SnapshotException ex2 = expectThrows( |
| 715 | + SnapshotException.class, |
| 716 | + () -> client().admin() |
| 717 | + .cluster() |
| 718 | + .prepareSnapshotStatus(repositoryName) |
| 719 | + .setSnapshots(snapshot2) |
| 720 | + .setIndices(index1, index2, index3) |
| 721 | + .execute() |
| 722 | + .actionGet() |
| 723 | + ); |
| 724 | + assertEquals(ex2.status(), RestStatus.NOT_FOUND); |
| 725 | + cause = String.format( |
| 726 | + "[%s:%s] indices [%s] missing in snapshot [%s]", |
| 727 | + repositoryName, |
| 728 | + snapshot2, |
| 729 | + String.join(", ", List.of(index2, index3)), |
| 730 | + snapshot2 |
| 731 | + ); |
| 732 | + assertEquals(cause, ex2.getMessage()); |
| 733 | + |
| 734 | + // failure due to too many shards requested |
| 735 | + logger.info("Set MAX_SHARDS_ALLOWED_IN_STATUS_API to a low value"); |
| 736 | + ClusterUpdateSettingsRequest updateSettingsRequest = new ClusterUpdateSettingsRequest(); |
| 737 | + updateSettingsRequest.persistentSettings(Settings.builder().put(MAX_SHARDS_ALLOWED_IN_STATUS_API.getKey(), 2)); |
| 738 | + assertAcked(client().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); |
| 739 | + |
| 740 | + SnapshotException ex3 = expectThrows( |
| 741 | + SnapshotException.class, |
| 742 | + () -> client().admin() |
| 743 | + .cluster() |
| 744 | + .prepareSnapshotStatus(repositoryName) |
| 745 | + .setSnapshots(snapshot1) |
| 746 | + .setIndices(index1, index2, index3) |
| 747 | + .execute() |
| 748 | + .actionGet() |
| 749 | + ); |
| 750 | + assertEquals(ex3.status(), RestStatus.REQUEST_ENTITY_TOO_LARGE); |
| 751 | + assertTrue(ex3.getMessage().endsWith(" is more than the maximum allowed value of shard count [2] for snapshot status request")); |
| 752 | + |
| 753 | + logger.info("Reset MAX_SHARDS_ALLOWED_IN_STATUS_API to default value"); |
| 754 | + updateSettingsRequest.persistentSettings(Settings.builder().putNull(MAX_SHARDS_ALLOWED_IN_STATUS_API.getKey())); |
| 755 | + assertAcked(internalCluster().client().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); |
| 756 | + |
| 757 | + }, 1, TimeUnit.MINUTES); |
| 758 | + } |
| 759 | + |
567 | 760 | private static SnapshotIndexShardStatus stateFirstShard(SnapshotStatus snapshotStatus, String indexName) {
|
568 | 761 | return snapshotStatus.getIndices().get(indexName).getShards().get(0);
|
569 | 762 | }
|
|
0 commit comments