Skip to content

Commit d0a8536

Browse files
committed
[CCR] Added more validation to follow index api.
Relates to elastic#30086
1 parent 1ccb34a commit d0a8536

File tree

2 files changed

+94
-9
lines changed

2 files changed

+94
-9
lines changed

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/FollowIndexAction.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.cluster.ClusterState;
2020
import org.elasticsearch.cluster.metadata.IndexMetaData;
2121
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
22+
import org.elasticsearch.cluster.metadata.MappingMetaData;
2223
import org.elasticsearch.cluster.service.ClusterService;
2324
import org.elasticsearch.common.inject.Inject;
2425
import org.elasticsearch.common.io.stream.StreamInput;
@@ -207,7 +208,7 @@ protected void doExecute(Request request, ActionListener<Response> listener) {
207208
*/
208209
void start(Request request, String clusterNameAlias, IndexMetaData leaderIndexMetadata, IndexMetaData followIndexMetadata,
209210
ActionListener<Response> handler) {
210-
validate (leaderIndexMetadata ,followIndexMetadata , request);
211+
validate(leaderIndexMetadata ,followIndexMetadata , request);
211212
final int numShards = followIndexMetadata.getNumberOfShards();
212213
final AtomicInteger counter = new AtomicInteger(numShards);
213214
final AtomicReferenceArray<Object> responses = new AtomicReferenceArray<>(followIndexMetadata.getNumberOfShards());
@@ -263,24 +264,43 @@ void finalizeResponse() {
263264
}
264265
}
265266

266-
267267
static void validate(IndexMetaData leaderIndex, IndexMetaData followIndex, Request request) {
268268
if (leaderIndex == null) {
269269
throw new IllegalArgumentException("leader index [" + request.leaderIndex + "] does not exist");
270270
}
271-
272271
if (followIndex == null) {
273272
throw new IllegalArgumentException("follow index [" + request.followIndex + "] does not exist");
274273
}
275274
if (leaderIndex.getSettings().getAsBoolean(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), false) == false) {
276275
throw new IllegalArgumentException("leader index [" + request.leaderIndex + "] does not have soft deletes enabled");
277276
}
278-
279277
if (leaderIndex.getNumberOfShards() != followIndex.getNumberOfShards()) {
280278
throw new IllegalArgumentException("leader index primary shards [" + leaderIndex.getNumberOfShards() +
281279
"] does not match with the number of shards of the follow index [" + followIndex.getNumberOfShards() + "]");
282280
}
283-
// TODO: other validation checks
281+
if (leaderIndex.getRoutingNumShards() != followIndex.getRoutingNumShards()) {
282+
throw new IllegalArgumentException("leader index number_of_routing_shards [" + leaderIndex.getRoutingNumShards() +
283+
"] does not match with the number_of_routing_shards of the follow index [" + followIndex.getRoutingNumShards() + "]");
284+
}
285+
if (leaderIndex.getState() != IndexMetaData.State.OPEN || followIndex.getState() != IndexMetaData.State.OPEN) {
286+
throw new IllegalArgumentException("leader and follow index must be open");
287+
}
288+
Map<String, Object> leaderMapping = getMapping(leaderIndex);
289+
Map<String, Object> followerMapping = getMapping(followIndex);
290+
if (leaderMapping.equals(followerMapping) == false) {
291+
throw new IllegalArgumentException("the leader and follower mappings must be identical");
292+
}
293+
Map<String, Settings> leaderAnalysisSettings = leaderIndex.getSettings().getGroups("index.analysis");
294+
Map<String, Settings> followerAnalysisSettings = followIndex.getSettings().getGroups("index.analysis");
295+
if (leaderAnalysisSettings.equals(followerAnalysisSettings) == false) {
296+
throw new IllegalArgumentException("the leader and follower index analysis settings must be identical");
297+
}
298+
}
299+
300+
private static Map<String, Object> getMapping(IndexMetaData indexMetaData) {
301+
assert indexMetaData.getMappings().size() == 1;
302+
MappingMetaData mappingMetaData = indexMetaData.getMappings().values().iterator().next().value;
303+
return mappingMetaData.getSourceAsMap();
284304
}
285305

286306
}

x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/FollowIndexActionTests.java

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@
77

88
import org.elasticsearch.Version;
99
import org.elasticsearch.cluster.metadata.IndexMetaData;
10+
import org.elasticsearch.cluster.metadata.IndexMetaData.State;
1011
import org.elasticsearch.common.collect.Tuple;
1112
import org.elasticsearch.common.settings.Settings;
1213
import org.elasticsearch.index.IndexSettings;
1314
import org.elasticsearch.test.ESTestCase;
1415

16+
import java.io.IOException;
17+
1518
import static org.hamcrest.Matchers.equalTo;
1619

1720
public class FollowIndexActionTests extends ESTestCase {
1821

19-
public void testValidation() {
22+
public void testValidation() throws IOException {
2023
FollowIndexAction.Request request = new FollowIndexAction.Request();
2124
request.setLeaderIndex("index1");
2225
request.setFollowIndex("index2");
@@ -45,22 +48,84 @@ public void testValidation() {
4548
assertThat(e.getMessage(),
4649
equalTo("leader index primary shards [5] does not match with the number of shards of the follow index [4]"));
4750
}
51+
{
52+
IndexMetaData leaderIMD = createIMD("index1", State.CLOSE, "{}", 5,
53+
new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"));
54+
IndexMetaData followIMD = createIMD("index2", State.OPEN, "{}", 5,
55+
new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"));
56+
Exception e = expectThrows(IllegalArgumentException.class,
57+
() -> FollowIndexAction.validate(leaderIMD, followIMD, request));
58+
assertThat(e.getMessage(), equalTo("leader and follow index must be open"));
59+
}
60+
{
61+
IndexMetaData leaderIMD = createIMD("index1", State.OPEN, "{\"properties\": {\"field\": {\"type\": \"keyword\"}}}", 5,
62+
new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"));
63+
IndexMetaData followIMD = createIMD("index2", State.OPEN, "{\"properties\": {\"field\": {\"type\": \"text\"}}}", 5);
64+
Exception e = expectThrows(IllegalArgumentException.class,
65+
() -> FollowIndexAction.validate(leaderIMD, followIMD, request));
66+
assertThat(e.getMessage(), equalTo("the leader and follower mappings must be identical"));
67+
}
68+
{
69+
String mapping = "{\"properties\": {\"field\": {\"type\": \"text\", \"analyzer\": \"my_analyzer\"}}}";
70+
IndexMetaData leaderIMD = createIMD("index1", State.OPEN, mapping, 5,
71+
new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"),
72+
new Tuple<>("index.analysis.analyzer.my_analyzer.type", "custom"),
73+
new Tuple<>("index.analysis.analyzer.my_analyzer.tokenizer", "whitespace"));
74+
IndexMetaData followIMD = createIMD("index2", State.OPEN, mapping, 5,
75+
new Tuple<>("index.analysis.analyzer.my_analyzer.type", "custom"),
76+
new Tuple<>("index.analysis.analyzer.my_analyzer.tokenizer", "standard"));
77+
Exception e = expectThrows(IllegalArgumentException.class,
78+
() -> FollowIndexAction.validate(leaderIMD, followIMD, request));
79+
assertThat(e.getMessage(), equalTo("the leader and follower index analysis settings must be identical"));
80+
}
4881
{
4982
IndexMetaData leaderIMD = createIMD("index1", 5, new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"));
5083
IndexMetaData followIMD = createIMD("index2", 5);
5184
FollowIndexAction.validate(leaderIMD, followIMD, request);
5285
}
86+
{
87+
String mapping = "{\"properties\": {\"field\": {\"type\": \"text\", \"analyzer\": \"my_analyzer\"}}}";
88+
IndexMetaData leaderIMD = createIMD("index1", State.OPEN, mapping, 5,
89+
new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"),
90+
new Tuple<>("index.analysis.analyzer.my_analyzer.type", "custom"),
91+
new Tuple<>("index.analysis.analyzer.my_analyzer.tokenizer", "standard"));
92+
IndexMetaData followIMD = createIMD("index2", State.OPEN, mapping, 5,
93+
new Tuple<>("index.analysis.analyzer.my_analyzer.type", "custom"),
94+
new Tuple<>("index.analysis.analyzer.my_analyzer.tokenizer", "standard"));
95+
FollowIndexAction.validate(leaderIMD, followIMD, request);
96+
}
97+
{
98+
String mapping = "{\"properties\": {\"field\": {\"type\": \"text\", \"analyzer\": \"my_analyzer\"}}}";
99+
IndexMetaData leaderIMD = createIMD("index1", State.OPEN, mapping, 5,
100+
new Tuple<>(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"),
101+
new Tuple<>(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "1s"),
102+
new Tuple<>("index.analysis.analyzer.my_analyzer.type", "custom"),
103+
new Tuple<>("index.analysis.analyzer.my_analyzer.tokenizer", "standard"));
104+
IndexMetaData followIMD = createIMD("index2", State.OPEN, mapping, 5,
105+
new Tuple<>(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "10s"),
106+
new Tuple<>("index.analysis.analyzer.my_analyzer.type", "custom"),
107+
new Tuple<>("index.analysis.analyzer.my_analyzer.tokenizer", "standard"));
108+
FollowIndexAction.validate(leaderIMD, followIMD, request);
109+
}
53110
}
54-
55-
private static IndexMetaData createIMD(String index, int numShards, Tuple<?, ?>... settings) {
111+
112+
private static IndexMetaData createIMD(String index, int numShards, Tuple<?, ?>... settings) throws IOException {
113+
return createIMD(index, State.OPEN, "{}", numShards, settings);
114+
}
115+
116+
private static IndexMetaData createIMD(String index, State state, String mapping, int numShards,
117+
Tuple<?, ?>... settings) throws IOException {
56118
Settings.Builder settingsBuilder = settings(Version.CURRENT);
57119
for (Tuple<?, ?> setting : settings) {
58120
settingsBuilder.put((String) setting.v1(), (String) setting.v2());
59121
}
60122
return IndexMetaData.builder(index).settings(settingsBuilder)
61123
.numberOfShards(numShards)
124+
.state(state)
62125
.numberOfReplicas(0)
63-
.setRoutingNumShards(numShards).build();
126+
.setRoutingNumShards(numShards)
127+
.putMapping("_doc", mapping)
128+
.build();
64129
}
65130

66131
}

0 commit comments

Comments
 (0)