|
12 | 12 | import org.apache.logging.log4j.Level;
|
13 | 13 | import org.elasticsearch.action.ActionFuture;
|
14 | 14 | import org.elasticsearch.action.ActionRequestBuilder;
|
| 15 | +import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; |
| 16 | +import org.elasticsearch.action.admin.cluster.allocation.TransportClusterAllocationExplainAction; |
15 | 17 | import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
|
16 | 18 | import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
17 | 19 | import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
|
|
20 | 22 | import org.elasticsearch.cluster.block.ClusterBlocks;
|
21 | 23 | import org.elasticsearch.cluster.metadata.IndexMetadata;
|
22 | 24 | import org.elasticsearch.cluster.metadata.MappingMetadata;
|
| 25 | +import org.elasticsearch.cluster.routing.allocation.decider.Decision; |
| 26 | +import org.elasticsearch.common.Strings; |
23 | 27 | import org.elasticsearch.common.settings.Settings;
|
24 | 28 | import org.elasticsearch.common.unit.ByteSizeUnit;
|
25 | 29 | import org.elasticsearch.core.TimeValue;
|
|
41 | 45 | import java.util.List;
|
42 | 46 | import java.util.Locale;
|
43 | 47 | import java.util.Map;
|
| 48 | +import java.util.Set; |
44 | 49 | import java.util.concurrent.TimeUnit;
|
45 | 50 | import java.util.stream.Collectors;
|
46 | 51 | import java.util.stream.IntStream;
|
|
63 | 68 | import static org.hamcrest.Matchers.not;
|
64 | 69 | import static org.hamcrest.Matchers.notNullValue;
|
65 | 70 | import static org.hamcrest.Matchers.nullValue;
|
| 71 | +import static org.hamcrest.Matchers.startsWith; |
66 | 72 |
|
67 | 73 | public class RestoreSnapshotIT extends AbstractSnapshotIntegTestCase {
|
68 | 74 |
|
@@ -1025,4 +1031,75 @@ public void testNoWarningsOnRestoreOverClosedIndex() throws IllegalAccessExcepti
|
1025 | 1031 | mockLog.assertAllExpectationsMatched();
|
1026 | 1032 | }
|
1027 | 1033 | }
|
| 1034 | + |
| 1035 | + public void testExplainUnassigableDuringRestore() { |
| 1036 | + final String repoName = "repo-" + randomIdentifier(); |
| 1037 | + createRepository(repoName, FsRepository.TYPE); |
| 1038 | + final String indexName = "index-" + randomIdentifier(); |
| 1039 | + createIndexWithContent(indexName); |
| 1040 | + final String snapshotName = "snapshot-" + randomIdentifier(); |
| 1041 | + createSnapshot(repoName, snapshotName, List.of(indexName)); |
| 1042 | + assertAcked(indicesAdmin().prepareDelete(indexName)); |
| 1043 | + |
| 1044 | + final RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot( |
| 1045 | + TEST_REQUEST_TIMEOUT, |
| 1046 | + repoName, |
| 1047 | + snapshotName |
| 1048 | + ) |
| 1049 | + .setIndices(indexName) |
| 1050 | + .setRestoreGlobalState(false) |
| 1051 | + .setWaitForCompletion(true) |
| 1052 | + .setIndexSettings( |
| 1053 | + Settings.builder().put(IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX + "._name", "not-a-node-" + randomIdentifier()) |
| 1054 | + ) |
| 1055 | + .get(); |
| 1056 | + |
| 1057 | + logger.info("--> restoreSnapshotResponse: {}", Strings.toString(restoreSnapshotResponse, true, true)); |
| 1058 | + assertThat(restoreSnapshotResponse.getRestoreInfo().failedShards(), greaterThan(0)); |
| 1059 | + |
| 1060 | + final var clusterExplainResponse1 = client().execute( |
| 1061 | + TransportClusterAllocationExplainAction.TYPE, |
| 1062 | + new ClusterAllocationExplainRequest(TEST_REQUEST_TIMEOUT).setIndex(indexName).setShard(0).setPrimary(true) |
| 1063 | + ).actionGet(); |
| 1064 | + |
| 1065 | + logger.info("--> clusterExplainResponse1: {}", Strings.toString(clusterExplainResponse1, true, true)); |
| 1066 | + for (var nodeDecision : clusterExplainResponse1.getExplanation() |
| 1067 | + .getShardAllocationDecision() |
| 1068 | + .getAllocateDecision() |
| 1069 | + .getNodeDecisions()) { |
| 1070 | + assertEquals( |
| 1071 | + Set.of("restore_in_progress", "filter"), |
| 1072 | + nodeDecision.getCanAllocateDecision().getDecisions().stream().map(Decision::label).collect(Collectors.toSet()) |
| 1073 | + ); |
| 1074 | + } |
| 1075 | + |
| 1076 | + updateIndexSettings(Settings.builder().putNull(IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX + "._name"), indexName); |
| 1077 | + |
| 1078 | + final var clusterExplainResponse2 = client().execute( |
| 1079 | + TransportClusterAllocationExplainAction.TYPE, |
| 1080 | + new ClusterAllocationExplainRequest(TEST_REQUEST_TIMEOUT).setIndex(indexName).setShard(0).setPrimary(true) |
| 1081 | + ).actionGet(); |
| 1082 | + |
| 1083 | + logger.info("--> clusterExplainResponse2: {}", Strings.toString(clusterExplainResponse2, true, true)); |
| 1084 | + for (var nodeDecision : clusterExplainResponse2.getExplanation() |
| 1085 | + .getShardAllocationDecision() |
| 1086 | + .getAllocateDecision() |
| 1087 | + .getNodeDecisions()) { |
| 1088 | + assertEquals( |
| 1089 | + Set.of("restore_in_progress"), |
| 1090 | + nodeDecision.getCanAllocateDecision().getDecisions().stream().map(Decision::label).collect(Collectors.toSet()) |
| 1091 | + ); |
| 1092 | + assertEquals( |
| 1093 | + Set.of("restore_in_progress"), |
| 1094 | + nodeDecision.getCanAllocateDecision().getDecisions().stream().map(Decision::label).collect(Collectors.toSet()) |
| 1095 | + ); |
| 1096 | + assertThat( |
| 1097 | + nodeDecision.getCanAllocateDecision().getDecisions().get(0).getExplanation(), |
| 1098 | + startsWith( |
| 1099 | + "Restore from snapshot failed because the configured constraints prevented allocation on any of the available nodes. " |
| 1100 | + + "Please check constraints applied in index and cluster settings, then retry the restore." |
| 1101 | + ) |
| 1102 | + ); |
| 1103 | + } |
| 1104 | + } |
1028 | 1105 | }
|
0 commit comments