Skip to content

Commit fc498f6

Browse files
probakowskidakrone
andauthored
[7.x] Add validation for component templates (#54023) (#54118)
* Add validation for component templates (#54023) * Add validation for component templates This change adds validation to make sure that settings and mappings are correct in component template. It's done the same way as in index templates - code is reused. Reletes to #53101 * Fix checkstyle violation * Update server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java Co-Authored-By: Lee Hinman <[email protected]> * Update server/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateServiceTests.java Co-Authored-By: Lee Hinman <[email protected]> * Update server/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateServiceTests.java Co-Authored-By: Lee Hinman <[email protected]> * Update server/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateServiceTests.java Co-Authored-By: Lee Hinman <[email protected]> * Update server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java Co-Authored-By: Lee Hinman <[email protected]> Co-authored-by: Lee Hinman <[email protected]> * Adjusted to 7.7 * unused import fixed * npe fixeD * change exception type Co-authored-by: Lee Hinman <[email protected]>
1 parent 025857d commit fc498f6

File tree

4 files changed

+94
-54
lines changed

4 files changed

+94
-54
lines changed

server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.elasticsearch.cluster.service.ClusterService;
3535
import org.elasticsearch.common.inject.Inject;
3636
import org.elasticsearch.common.io.stream.StreamInput;
37+
import org.elasticsearch.common.settings.IndexScopedSettings;
3738
import org.elasticsearch.common.settings.Settings;
3839
import org.elasticsearch.threadpool.ThreadPool;
3940
import org.elasticsearch.transport.TransportService;
@@ -44,14 +45,17 @@ public class TransportPutComponentTemplateAction
4445
extends TransportMasterNodeAction<PutComponentTemplateAction.Request, AcknowledgedResponse> {
4546

4647
private final MetaDataIndexTemplateService indexTemplateService;
48+
private final IndexScopedSettings indexScopedSettings;
4749

4850
@Inject
4951
public TransportPutComponentTemplateAction(TransportService transportService, ClusterService clusterService,
5052
ThreadPool threadPool, MetaDataIndexTemplateService indexTemplateService,
51-
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
53+
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
54+
IndexScopedSettings indexScopedSettings) {
5255
super(PutComponentTemplateAction.NAME, transportService, clusterService, threadPool, actionFilters,
5356
PutComponentTemplateAction.Request::new, indexNameExpressionResolver);
5457
this.indexTemplateService = indexTemplateService;
58+
this.indexScopedSettings = indexScopedSettings;
5559
}
5660

5761
@Override
@@ -77,8 +81,10 @@ protected void masterOperation(final PutComponentTemplateAction.Request request,
7781
Template template = componentTemplate.template();
7882
// Normalize the index settings if necessary
7983
if (template.settings() != null) {
80-
Settings.Builder settings = Settings.builder().put(template.settings()).normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);
81-
template = new Template(settings.build(), template.mappings(), template.aliases());
84+
Settings.Builder builder = Settings.builder().put(template.settings()).normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);
85+
Settings settings = builder.build();
86+
indexScopedSettings.validate(settings, true);
87+
template = new Template(settings, template.mappings(), template.aliases());
8288
componentTemplate = new ComponentTemplate(template, componentTemplate.version(), componentTemplate.metadata());
8389
}
8490
indexTemplateService.putComponentTemplate(request.cause(), request.create(), request.name(), request.masterNodeTimeout(),

server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@
3535
import org.elasticsearch.common.Strings;
3636
import org.elasticsearch.common.UUIDs;
3737
import org.elasticsearch.common.ValidationException;
38+
import org.elasticsearch.common.compress.CompressedXContent;
3839
import org.elasticsearch.common.inject.Inject;
3940
import org.elasticsearch.common.regex.Regex;
4041
import org.elasticsearch.common.settings.IndexScopedSettings;
4142
import org.elasticsearch.common.settings.Settings;
4243
import org.elasticsearch.common.unit.TimeValue;
4344
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
45+
import org.elasticsearch.common.xcontent.XContentHelper;
46+
import org.elasticsearch.common.xcontent.XContentType;
4447
import org.elasticsearch.index.Index;
4548
import org.elasticsearch.index.IndexService;
4649
import org.elasticsearch.index.mapper.MapperParsingException;
@@ -156,7 +159,7 @@ public void onFailure(String source, Exception e) {
156159
}
157160

158161
@Override
159-
public ClusterState execute(ClusterState currentState) {
162+
public ClusterState execute(ClusterState currentState) throws Exception {
160163
return addComponentTemplate(currentState, create, name, template);
161164
}
162165

@@ -168,14 +171,18 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS
168171
}
169172

170173
// Package visible for testing
171-
static ClusterState addComponentTemplate(final ClusterState currentState, final boolean create,
172-
final String name, final ComponentTemplate template) {
174+
ClusterState addComponentTemplate(final ClusterState currentState, final boolean create,
175+
final String name, final ComponentTemplate template) throws Exception {
173176
if (create && currentState.metaData().componentTemplates().containsKey(name)) {
174177
throw new IllegalArgumentException("component template [" + name + "] already exists");
175178
}
176179

177-
// TODO: validation of component template
178-
// validateAndAddTemplate(request, templateBuilder, indicesService, xContentRegistry);
180+
CompressedXContent mappings = template.template().mappings();
181+
Map<String, Object> mappingsArray = Collections.emptyMap();
182+
if(mappings != null) {
183+
mappingsArray = XContentHelper.convertToMap(XContentType.JSON.xContent(), mappings.string(), true);
184+
}
185+
validateTemplate(template.template().settings(), Collections.singletonMap("_doc", mappingsArray), indicesService);
179186

180187
logger.info("adding component template [{}]", name);
181188
return ClusterState.builder(currentState)
@@ -276,7 +283,22 @@ public ClusterState execute(ClusterState currentState) throws Exception {
276283
throw new IllegalArgumentException("index_template [" + request.name + "] already exists");
277284
}
278285

279-
validateAndAddTemplate(request, templateBuilder, indicesService, xContentRegistry);
286+
templateBuilder.order(request.order);
287+
templateBuilder.version(request.version);
288+
templateBuilder.patterns(request.indexPatterns);
289+
templateBuilder.settings(request.settings);
290+
291+
Map<String, Map<String, Object>> mappingsForValidation = new HashMap<>();
292+
for (Map.Entry<String, String> entry : request.mappings.entrySet()) {
293+
try {
294+
templateBuilder.putMapping(entry.getKey(), entry.getValue());
295+
} catch (Exception e) {
296+
throw new MapperParsingException("Failed to parse mapping [{}]: {}", e, entry.getKey(), e.getMessage());
297+
}
298+
mappingsForValidation.put(entry.getKey(), MapperService.parseMapping(xContentRegistry, entry.getValue()));
299+
}
300+
301+
validateTemplate(request.settings, mappingsForValidation, indicesService);
280302

281303
for (Alias alias : request.aliases) {
282304
AliasMetaData aliasMetaData = AliasMetaData.builder(alias.name()).filter(alias.filter())
@@ -357,20 +379,20 @@ public static List<IndexTemplateMetaData> findTemplates(MetaData metaData, Strin
357379
return matchedTemplates;
358380
}
359381

360-
private static void validateAndAddTemplate(final PutRequest request, IndexTemplateMetaData.Builder templateBuilder,
361-
IndicesService indicesService, NamedXContentRegistry xContentRegistry) throws Exception {
382+
private static void validateTemplate(Settings settings, Map<String, Map<String, Object>> mappings,
383+
IndicesService indicesService) throws Exception {
362384
Index createdIndex = null;
363385
final String temporaryIndexName = UUIDs.randomBase64UUID();
364386
try {
365387
// use the provided values, otherwise just pick valid dummy values
366-
int dummyPartitionSize = IndexMetaData.INDEX_ROUTING_PARTITION_SIZE_SETTING.get(request.settings);
367-
int dummyShards = request.settings.getAsInt(IndexMetaData.SETTING_NUMBER_OF_SHARDS,
388+
int dummyPartitionSize = IndexMetaData.INDEX_ROUTING_PARTITION_SIZE_SETTING.get(settings);
389+
int dummyShards = settings.getAsInt(IndexMetaData.SETTING_NUMBER_OF_SHARDS,
368390
dummyPartitionSize == 1 ? 1 : dummyPartitionSize + 1);
369391

370392
//create index service for parsing and validating "mappings"
371393
Settings dummySettings = Settings.builder()
372394
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
373-
.put(request.settings)
395+
.put(settings)
374396
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, dummyShards)
375397
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
376398
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
@@ -380,22 +402,7 @@ private static void validateAndAddTemplate(final PutRequest request, IndexTempla
380402
IndexService dummyIndexService = indicesService.createIndex(tmpIndexMetadata, Collections.emptyList(), false);
381403
createdIndex = dummyIndexService.index();
382404

383-
templateBuilder.order(request.order);
384-
templateBuilder.version(request.version);
385-
templateBuilder.patterns(request.indexPatterns);
386-
templateBuilder.settings(request.settings);
387-
388-
Map<String, Map<String, Object>> mappingsForValidation = new HashMap<>();
389-
for (Map.Entry<String, String> entry : request.mappings.entrySet()) {
390-
try {
391-
templateBuilder.putMapping(entry.getKey(), entry.getValue());
392-
} catch (Exception e) {
393-
throw new MapperParsingException("Failed to parse mapping [{}]: {}", e, entry.getKey(), e.getMessage());
394-
}
395-
mappingsForValidation.put(entry.getKey(), MapperService.parseMapping(xContentRegistry, entry.getValue()));
396-
}
397-
398-
dummyIndexService.mapperService().merge(mappingsForValidation, MergeReason.MAPPING_UPDATE);
405+
dummyIndexService.mapperService().merge(mappings, MergeReason.MAPPING_UPDATE);
399406

400407
} finally {
401408
if (createdIndex != null) {

server/src/test/java/org/elasticsearch/cluster/metadata/ComponentTemplateTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static ComponentTemplate randomInstance() {
8585
return new ComponentTemplate(template, randomBoolean() ? null : randomNonNegativeLong(), meta);
8686
}
8787

88-
private static Map<String, AliasMetaData> randomAliases() {
88+
public static Map<String, AliasMetaData> randomAliases() {
8989
String aliasName = randomAlphaOfLength(5);
9090
AliasMetaData aliasMeta = AliasMetaData.builder(aliasName)
9191
.filter(Collections.singletonMap(randomAlphaOfLength(2), randomAlphaOfLength(2)))

server/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateServiceTests.java

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919

2020
package org.elasticsearch.cluster.metadata;
2121

22+
import org.elasticsearch.ElasticsearchParseException;
2223
import org.elasticsearch.action.admin.indices.alias.Alias;
2324
import org.elasticsearch.cluster.ClusterState;
2425
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService.PutRequest;
2526
import org.elasticsearch.cluster.service.ClusterService;
2627
import org.elasticsearch.common.Strings;
28+
import org.elasticsearch.common.compress.CompressedXContent;
2729
import org.elasticsearch.common.settings.IndexScopedSettings;
2830
import org.elasticsearch.common.settings.Settings;
2931
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
@@ -37,6 +39,7 @@
3739
import java.util.ArrayList;
3840
import java.util.Arrays;
3941
import java.util.Collections;
42+
import java.util.HashMap;
4043
import java.util.HashSet;
4144
import java.util.List;
4245
import java.util.Set;
@@ -197,21 +200,41 @@ public void testPutGlobalTemplateWithIndexHiddenSetting() throws Exception {
197200
assertThat(errors.get(0).getMessage(), containsString("global templates may not specify the setting index.hidden"));
198201
}
199202

200-
public void testAddComponentTemplate() {
203+
public void testAddComponentTemplate() throws Exception{
204+
MetaDataIndexTemplateService metaDataIndexTemplateService = getMetaDataIndexTemplateService();
201205
ClusterState state = ClusterState.EMPTY_STATE;
202-
ComponentTemplate template = ComponentTemplateTests.randomInstance();
203-
state = MetaDataIndexTemplateService.addComponentTemplate(state, false, "foo", template);
206+
Template template = new Template(Settings.builder().build(), null, ComponentTemplateTests.randomAliases());
207+
ComponentTemplate componentTemplate = new ComponentTemplate(template, 1L, new HashMap<>());
208+
state = metaDataIndexTemplateService.addComponentTemplate(state, false, "foo", componentTemplate);
204209

205210
assertNotNull(state.metaData().componentTemplates().get("foo"));
206-
assertThat(state.metaData().componentTemplates().get("foo"), equalTo(template));
211+
assertThat(state.metaData().componentTemplates().get("foo"), equalTo(componentTemplate));
207212

208213
final ClusterState throwState = ClusterState.builder(state).build();
209214
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
210-
() -> MetaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo", template));
215+
() -> metaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo", componentTemplate));
211216
assertThat(e.getMessage(), containsString("component template [foo] already exists"));
212217

213-
state = MetaDataIndexTemplateService.addComponentTemplate(state, randomBoolean(), "bar", template);
218+
state = metaDataIndexTemplateService.addComponentTemplate(state, randomBoolean(), "bar", componentTemplate);
214219
assertNotNull(state.metaData().componentTemplates().get("bar"));
220+
221+
template = new Template(Settings.builder().build(), new CompressedXContent("{\"invalid\"}"),
222+
ComponentTemplateTests.randomAliases());
223+
ComponentTemplate componentTemplate2 = new ComponentTemplate(template, 1L, new HashMap<>());
224+
expectThrows(ElasticsearchParseException.class,
225+
() -> metaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo2", componentTemplate2));
226+
227+
template = new Template(Settings.builder().build(), new CompressedXContent("{\"invalid\":\"invalid\"}"),
228+
ComponentTemplateTests.randomAliases());
229+
ComponentTemplate componentTemplate3 = new ComponentTemplate(template, 1L, new HashMap<>());
230+
expectThrows(MapperParsingException.class,
231+
() -> metaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo2", componentTemplate3));
232+
233+
template = new Template(Settings.builder().put("invalid", "invalid").build(), new CompressedXContent("{}"),
234+
ComponentTemplateTests.randomAliases());
235+
ComponentTemplate componentTemplate4 = new ComponentTemplate(template, 1L, new HashMap<>());
236+
expectThrows(IllegalArgumentException.class,
237+
() -> metaDataIndexTemplateService.addComponentTemplate(throwState, true, "foo2", componentTemplate4));
215238
}
216239

217240
private static List<Throwable> putTemplate(NamedXContentRegistry xContentRegistry, PutRequest request) {
@@ -247,23 +270,7 @@ public void onFailure(Exception e) {
247270
}
248271

249272
private List<Throwable> putTemplateDetail(PutRequest request) throws Exception {
250-
IndicesService indicesService = getInstanceFromNode(IndicesService.class);
251-
ClusterService clusterService = getInstanceFromNode(ClusterService.class);
252-
MetaDataCreateIndexService createIndexService = new MetaDataCreateIndexService(
253-
Settings.EMPTY,
254-
clusterService,
255-
indicesService,
256-
null,
257-
null,
258-
new Environment(builder().put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()).build(), null),
259-
IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
260-
null,
261-
xContentRegistry(),
262-
Collections.emptyList(),
263-
true);
264-
MetaDataIndexTemplateService service = new MetaDataIndexTemplateService(
265-
clusterService, createIndexService, new AliasValidator(), indicesService,
266-
new IndexScopedSettings(Settings.EMPTY, IndexScopedSettings.BUILT_IN_INDEX_SETTINGS), xContentRegistry());
273+
MetaDataIndexTemplateService service = getMetaDataIndexTemplateService();
267274

268275
final List<Throwable> throwables = new ArrayList<>();
269276
final CountDownLatch latch = new CountDownLatch(1);
@@ -282,4 +289,24 @@ public void onFailure(Exception e) {
282289
latch.await();
283290
return throwables;
284291
}
292+
293+
private MetaDataIndexTemplateService getMetaDataIndexTemplateService() {
294+
IndicesService indicesService = getInstanceFromNode(IndicesService.class);
295+
ClusterService clusterService = getInstanceFromNode(ClusterService.class);
296+
MetaDataCreateIndexService createIndexService = new MetaDataCreateIndexService(
297+
Settings.EMPTY,
298+
clusterService,
299+
indicesService,
300+
null,
301+
null,
302+
new Environment(builder().put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()).build(), null),
303+
IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
304+
null,
305+
xContentRegistry(),
306+
Collections.emptyList(),
307+
true);
308+
return new MetaDataIndexTemplateService(
309+
clusterService, createIndexService, new AliasValidator(), indicesService,
310+
new IndexScopedSettings(Settings.EMPTY, IndexScopedSettings.BUILT_IN_INDEX_SETTINGS), xContentRegistry());
311+
}
285312
}

0 commit comments

Comments
 (0)