3333import org .elasticsearch .common .settings .ClusterSettings ;
3434import org .elasticsearch .common .settings .Settings ;
3535import org .elasticsearch .common .util .concurrent .EsExecutors ;
36+ import org .elasticsearch .core .Nullable ;
3637import org .elasticsearch .core .UpdateForV10 ;
3738import org .elasticsearch .index .IndexService ;
3839import org .elasticsearch .index .IndexSettingProvider ;
4849import org .elasticsearch .transport .TransportService ;
4950import org .elasticsearch .xcontent .NamedXContentRegistry ;
5051
52+ import java .io .IOException ;
5153import java .time .Instant ;
54+ import java .util .ArrayList ;
5255import java .util .HashMap ;
5356import java .util .HashSet ;
5457import java .util .List ;
@@ -158,7 +161,6 @@ protected void localClusterStateOperation(
158161 listener .onResponse (new SimulateIndexTemplateResponse (null , null ));
159162 return ;
160163 }
161-
162164 final ProjectMetadata tempProjectMetadata = resolveTemporaryState (matchingTemplate , request .getIndexName (), projectWithTemplate );
163165 ComposableIndexTemplate templateV2 = tempProjectMetadata .templatesV2 ().get (matchingTemplate );
164166 assert templateV2 != null : "the matched template must exist" ;
@@ -167,6 +169,7 @@ protected void localClusterStateOperation(
167169 matchingTemplate ,
168170 request .getIndexName (),
169171 projectWithTemplate ,
172+ state .metadata ().dataStreams ().get (request .getIndexName ()),
170173 isDslOnlyMode ,
171174 xContentRegistry ,
172175 indicesService ,
@@ -233,44 +236,42 @@ public static ProjectMetadata resolveTemporaryState(
233236 /**
234237 * Take a template and index name as well as state where the template exists, and return a final
235238 * {@link Template} that represents all the resolved Settings, Mappings, Aliases and Lifecycle
239+ *
240+ * @param matchingTemplate
241+ * @param indexName
242+ * @param simulatedProject
243+ * @param dataStream Used to get any data stream mapping or settings overrides to merge into the returned template
244+ * @param isDslOnlyMode
245+ * @param xContentRegistry
246+ * @param indicesService
247+ * @param systemIndices
248+ * @param indexSettingProviders
249+ * @return
250+ * @throws Exception
236251 */
237252 public static Template resolveTemplate (
238253 final String matchingTemplate ,
239254 final String indexName ,
240255 final ProjectMetadata simulatedProject ,
256+ @ Nullable final DataStream dataStream ,
241257 final boolean isDslOnlyMode ,
242258 final NamedXContentRegistry xContentRegistry ,
243259 final IndicesService indicesService ,
244260 final SystemIndices systemIndices ,
245261 Set <IndexSettingProvider > indexSettingProviders
246262 ) throws Exception {
247- Settings templateSettings = resolveSettings (simulatedProject , matchingTemplate );
248-
249263 List <Map <String , AliasMetadata >> resolvedAliases = MetadataIndexTemplateService .resolveAliases (simulatedProject , matchingTemplate );
250264
251265 ComposableIndexTemplate template = simulatedProject .templatesV2 ().get (matchingTemplate );
266+ Settings templateSettings = collectSettings (simulatedProject , dataStream , matchingTemplate , template );
252267 // create the index with dummy settings in the cluster state so we can parse and validate the aliases
253268 Settings .Builder dummySettings = Settings .builder ()
254269 .put (IndexMetadata .SETTING_VERSION_CREATED , IndexVersion .current ())
255270 .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , 1 )
256271 .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 0 )
257272 .put (IndexMetadata .SETTING_INDEX_UUID , UUIDs .randomBase64UUID ());
258273
259- /*
260- * If the index name doesn't look like a data stream backing index, then MetadataCreateIndexService.collectV2Mappings() won't
261- * include data stream specific mappings in its response.
262- */
263- String simulatedIndexName = template .getDataStreamTemplate () != null
264- && indexName .startsWith (DataStream .BACKING_INDEX_PREFIX ) == false
265- ? DataStream .getDefaultBackingIndexName (indexName , 1 )
266- : indexName ;
267- List <CompressedXContent > mappings = MetadataCreateIndexService .collectV2Mappings (
268- null , // empty request mapping as the user can't specify any explicit mappings via the simulate api
269- simulatedProject ,
270- template ,
271- xContentRegistry ,
272- simulatedIndexName
273- );
274+ List <CompressedXContent > mappings = collectMappings (simulatedProject , dataStream , template , indexName , xContentRegistry );
274275
275276 // First apply settings sourced from index settings providers
276277 final var now = Instant .now ();
@@ -360,6 +361,73 @@ public static Template resolveTemplate(
360361 );
361362 }
362363
364+ /**
365+ * This method collects the mappings from the given template, pulling them from the given simulatedProject. If the template is a data
366+ * stream template and the given dataStream is not null, this method also appends any mapping overrides from the data stream itself.
367+ * @param simulatedProject Used to fetch the component templates referenced from the template
368+ * @param dataStream Used to fetch any mappings explicitly set on the data stream
369+ * @param template The template matching the index, used to fetch mappings
370+ * @param indexName The name of the index whose templates we are fetching
371+ * @param xContentRegistry Used to parse mappings if necessary
372+ * @return A list of matching mappings in ascending priority order
373+ * @throws IOException
374+ */
375+ private static List <CompressedXContent > collectMappings (
376+ ProjectMetadata simulatedProject ,
377+ @ Nullable DataStream dataStream ,
378+ ComposableIndexTemplate template ,
379+ String indexName ,
380+ NamedXContentRegistry xContentRegistry
381+ ) throws IOException {
382+ /*
383+ * If the index name doesn't look like a data stream backing index, then MetadataCreateIndexService.collectV2Mappings() won't
384+ * include data stream specific mappings in its response.
385+ */
386+ String simulatedIndexName = template .getDataStreamTemplate () != null
387+ && indexName .startsWith (DataStream .BACKING_INDEX_PREFIX ) == false
388+ ? DataStream .getDefaultBackingIndexName (indexName , 1 )
389+ : indexName ;
390+ List <CompressedXContent > mappings = MetadataCreateIndexService .collectV2Mappings (
391+ null , // empty request mapping as the user can't specify any explicit mappings via the simulate api
392+ simulatedProject ,
393+ template ,
394+ xContentRegistry , // This is never used since requestMappings is always null, but it is not marked explicitly as @Nullable
395+ simulatedIndexName
396+ );
397+ if (template .getDataStreamTemplate () != null && dataStream != null ) {
398+ CompressedXContent dataStreamMappingOverrides = dataStream .getMappings ();
399+ if (ComposableIndexTemplate .EMPTY_MAPPINGS .equals (dataStreamMappingOverrides ) == false ) {
400+ // The data stream has had mapping overrides applied, so include these
401+ mappings = new ArrayList <>(mappings );
402+ mappings .add (dataStreamMappingOverrides );
403+ }
404+ }
405+ return mappings ;
406+ }
407+
408+ /**
409+ * This method collects the settings from the given template using the given simulatedProjcet. If dataStream is not null, it also merges
410+ * in any settings overrides on the data stream itself.
411+ * @param simulatedProject The project metadata used to get the settings
412+ * @param dataStream If not null, this is used to get data stream settings overrides
413+ * @param templateName The name of the template
414+ * @param template The template, used to check whether this is a data strema template
415+ * @return The settings to be used
416+ */
417+ private static Settings collectSettings (
418+ final ProjectMetadata simulatedProject ,
419+ @ Nullable final DataStream dataStream ,
420+ String templateName ,
421+ ComposableIndexTemplate template
422+ ) {
423+ Settings templateSettings = resolveSettings (simulatedProject , templateName );
424+ if (template .getDataStreamTemplate () != null && dataStream != null ) {
425+ // The data stream has had settings overrides applied, so include them
426+ templateSettings = templateSettings .merge (dataStream .getSettings ());
427+ }
428+ return templateSettings ;
429+ }
430+
363431 private static IndexLongFieldRange getEventIngestedRange (String indexName , ProjectMetadata simulatedProject ) {
364432 final IndexMetadata indexMetadata = simulatedProject .index (indexName );
365433 return indexMetadata == null ? IndexLongFieldRange .NO_SHARDS : indexMetadata .getEventIngestedRange ();
0 commit comments