4747import  org .elasticsearch .index .mapper .MapperService ;
4848import  org .elasticsearch .index .mapper .MapperService .MergeReason ;
4949import  org .elasticsearch .index .mapper .RoutingFieldMapper ;
50- import  org .elasticsearch .index .shard .IndexLongFieldRange ;
5150import  org .elasticsearch .indices .IndexTemplateMissingException ;
5251import  org .elasticsearch .indices .IndicesService ;
5352import  org .elasticsearch .indices .InvalidIndexTemplateException ;
@@ -173,9 +172,12 @@ private abstract static class TemplateClusterStateUpdateTask implements ClusterS
173172        }
174173
175174        public  final  ClusterState  execute (ClusterState  currentState ) throws  Exception  {
176-             ProjectMetadata  metadata  = currentState .metadata ().getProject (projectId );
177-             ProjectMetadata  newMetadata  = execute (metadata );
178-             return  metadata  == newMetadata  ? currentState  : ClusterState .builder (currentState ).putProjectMetadata (newMetadata ).build ();
175+             ProjectMetadata  currentProject  = currentState .metadata ().getProject (projectId );
176+             ProjectMetadata  newProject  = execute (currentProject );
177+             if  (currentProject  == newProject ) {
178+                 return  currentState ;
179+             }
180+             return  ClusterState .builder (currentState ).metadata (currentState .metadata ().withUpdatedProject (newProject )).build ();
179181        }
180182
181183        public  abstract  ProjectMetadata  execute (ProjectMetadata  currentProject ) throws  Exception ;
@@ -384,19 +386,17 @@ public ProjectMetadata addComponentTemplate(
384386        }
385387
386388        validateTemplate (finalSettings , wrappedMappings , indicesService );
387-         validate (name , finalComponentTemplate );
389+         validate (name , finalComponentTemplate . template (),  List . of (),  null );
388390
391+         ProjectMetadata  projectWithComponentTemplateAdded  = ProjectMetadata .builder (project ).put (name , finalComponentTemplate ).build ();
389392        // Validate all composable index templates that use this component template 
390-         if  (templatesUsingComponent .size () > 0 ) {
391-             ProjectMetadata  tempProjectWithComponentTemplateAdded  = ProjectMetadata .builder (project )
392-                 .put (name , finalComponentTemplate )
393-                 .build ();
393+         if  (templatesUsingComponent .isEmpty () == false ) {
394394            Exception  validationFailure  = null ;
395395            for  (Map .Entry <String , ComposableIndexTemplate > entry  : templatesUsingComponent .entrySet ()) {
396396                final  String  composableTemplateName  = entry .getKey ();
397397                final  ComposableIndexTemplate  composableTemplate  = entry .getValue ();
398398                try  {
399-                     validateIndexTemplateV2 (tempProjectWithComponentTemplateAdded , composableTemplateName , composableTemplate );
399+                     validateIndexTemplateV2 (projectWithComponentTemplateAdded , composableTemplateName , composableTemplate );
400400                } catch  (Exception  e ) {
401401                    if  (validationFailure  == null ) {
402402                        validationFailure  = new  IllegalArgumentException (
@@ -426,7 +426,7 @@ public ProjectMetadata addComponentTemplate(
426426        }
427427
428428        logger .info ("{} component template [{}]" , existing  == null  ? "adding"  : "updating" , name );
429-         return  ProjectMetadata . builder ( project ). put ( name ,  finalComponentTemplate ). build () ;
429+         return  projectWithComponentTemplateAdded ;
430430    }
431431
432432    /** 
@@ -784,8 +784,9 @@ void validateIndexTemplateV2(ProjectMetadata projectMetadata, String name, Compo
784784        var  finalTemplate  = indexTemplate .template ();
785785        final  var  now  = instantSource .instant ();
786786
787-         final  var  combinedMappings  = collectMappings (indexTemplate , projectMetadata .componentTemplates (), "tmp_idx" );
788-         final  var  combinedSettings  = resolveSettings (indexTemplate , projectMetadata .componentTemplates ());
787+         final  var  componentTemplates  = projectMetadata .componentTemplates ();
788+         final  var  combinedMappings  = collectMappings (indexTemplate , componentTemplates , "tmp_idx" );
789+         final  var  combinedSettings  = resolveSettings (indexTemplate , componentTemplates );
789790        var  additionalSettingsBuilder  = Settings .builder ();
790791        ImmutableOpenMap .Builder <String , Map <String , String >> customMetadataBuilder  = ImmutableOpenMap .builder ();
791792        for  (var  provider  : indexSettingProviders ) {
@@ -820,11 +821,11 @@ void validateIndexTemplateV2(ProjectMetadata projectMetadata, String name, Compo
820821
821822        validate (name , templateToValidate , additionalSettings );
822823        validateDataStreamsStillReferenced (projectMetadata , name , templateToValidate );
823-         validateLifecycle (projectMetadata , name , templateToValidate , globalRetentionSettings .get (false ));
824-         validateDataStreamOptions (projectMetadata , name , templateToValidate , globalRetentionSettings .get (true ));
824+         validateLifecycle (componentTemplates , name , templateToValidate , globalRetentionSettings .get (false ));
825+         validateDataStreamOptions (componentTemplates , name , templateToValidate , globalRetentionSettings .get (true ));
825826
826827        if  (templateToValidate .isDeprecated () == false ) {
827-             validateUseOfDeprecatedComponentTemplates (name , templateToValidate , projectMetadata . componentTemplates () );
828+             validateUseOfDeprecatedComponentTemplates (name , templateToValidate , componentTemplates );
828829            validateUseOfDeprecatedIngestPipelines (name , projectMetadata .custom (IngestMetadata .TYPE ), combinedSettings );
829830            // TODO come up with a plan how to validate usage of deprecated ILM policies 
830831            // we don't have access to the core/main plugin here so we can't use the IndexLifecycleMetadata type 
@@ -900,12 +901,12 @@ private void emitWarningIfPipelineIsDeprecated(String name, Map<String, Pipeline
900901
901902    // Visible for testing 
902903    static  void  validateLifecycle (
903-         ProjectMetadata   project ,
904+         Map < String ,  ComponentTemplate >  componentTemplates ,
904905        String  indexTemplateName ,
905906        ComposableIndexTemplate  template ,
906907        @ Nullable  DataStreamGlobalRetention  globalRetention 
907908    ) {
908-         DataStreamLifecycle .Builder  builder  = resolveLifecycle (template , project . componentTemplates () );
909+         DataStreamLifecycle .Builder  builder  = resolveLifecycle (template , componentTemplates );
909910        if  (builder  != null ) {
910911            if  (template .getDataStreamTemplate () == null ) {
911912                throw  new  IllegalArgumentException (
@@ -925,12 +926,12 @@ static void validateLifecycle(
925926
926927    // Visible for testing 
927928    static  void  validateDataStreamOptions (
928-         ProjectMetadata   projectMetadata ,
929+         Map < String ,  ComponentTemplate >  componentTemplates ,
929930        String  indexTemplateName ,
930931        ComposableIndexTemplate  template ,
931932        DataStreamGlobalRetention  globalRetention 
932933    ) {
933-         DataStreamOptions .Builder  dataStreamOptionsBuilder  = resolveDataStreamOptions (template , projectMetadata . componentTemplates () );
934+         DataStreamOptions .Builder  dataStreamOptionsBuilder  = resolveDataStreamOptions (template , componentTemplates );
934935        if  (dataStreamOptionsBuilder  != null ) {
935936            if  (template .getDataStreamTemplate () == null ) {
936937                throw  new  IllegalArgumentException (
@@ -971,18 +972,18 @@ private static void validateDataStreamsStillReferenced(
971972            .map (Map .Entry ::getKey )
972973            .collect (Collectors .toSet ());
973974
974-         Function <ProjectMetadata ,  Set <String >> findUnreferencedDataStreams  = meta  -> {
975+         Function <Map < String ,  ComposableIndexTemplate >,  Set <String >> findUnreferencedDataStreams  = composableTemplates  -> {
975976            final  Set <String > unreferenced  = new  HashSet <>();
976977            // For each data stream that we have, see whether it's covered by a different 
977978            // template (which is great), or whether it's now uncovered by any template 
978979            for  (String  dataStream  : dataStreams ) {
979-                 final  String  matchingTemplate  = findV2Template (meta ,  dataStream , false );
980+                 final  String  matchingTemplate  = findV2Template (project ,  composableTemplates . entrySet (),  dataStream ,  false , false );
980981                if  (matchingTemplate  == null ) {
981982                    unreferenced .add (dataStream );
982983                } else  {
983984                    // We found a template that still matches, great! Buuuuttt... check whether it 
984985                    // is a data stream template, as it's only useful if it has a data stream definition 
985-                     if  (meta . templatesV2 () .get (matchingTemplate ).getDataStreamTemplate () == null ) {
986+                     if  (composableTemplates .get (matchingTemplate ).getDataStreamTemplate () == null ) {
986987                        unreferenced .add (dataStream );
987988                    }
988989                }
@@ -991,12 +992,13 @@ private static void validateDataStreamsStillReferenced(
991992        };
992993
993994        // Find data streams that are currently unreferenced 
994-         final  Set <String > currentlyUnreferenced  = findUnreferencedDataStreams .apply (project );
995+         final  Set <String > currentlyUnreferenced  = findUnreferencedDataStreams .apply (project . templatesV2 () );
995996
996-         // Generate a metadata as if the new template were actually in the cluster state 
997-         final  ProjectMetadata  updatedMetadata  = ProjectMetadata .builder (project ).put (templateName , newTemplate ).build ();
997+         // Generate a map as if the new template were actually in the cluster state 
998+         final  var  updatedTemplatesMap  = new  HashMap <>(project .templatesV2 ());
999+         updatedTemplatesMap .put (templateName , newTemplate );
9981000        // Find the data streams that would be unreferenced now that the template is updated/added 
999-         final  Set <String > newlyUnreferenced  = findUnreferencedDataStreams .apply (updatedMetadata );
1001+         final  Set <String > newlyUnreferenced  = findUnreferencedDataStreams .apply (updatedTemplatesMap );
10001002
10011003        // If we found any data streams that used to be covered, but will no longer be covered by 
10021004        // changing this template, then blow up with as much helpful information as we can muster 
@@ -1226,7 +1228,7 @@ static Set<String> dataStreamsExclusivelyUsingTemplates(final ProjectMetadata pr
12261228                return  candidates .stream ()
12271229                    .noneMatch (
12281230                        template  -> templateNames .contains (template .v1 ()) == false 
1229-                             && isGlobalAndHasIndexHiddenSetting (projectMetadata ,  template .v2 (), template . v1 ()) == false 
1231+                             && isGlobalAndHasIndexHiddenSetting (template .v2 (), projectMetadata . componentTemplates ()) == false 
12301232                    );
12311233            })
12321234            .map (DataStream ::getName )
@@ -1514,7 +1516,7 @@ private static String findV2Template(
15141516        // a restored index cluster state that modified a component template used by this global template such that it has this setting) 
15151517        // we will fail and the user will have to update the index template and remove this setting or update the corresponding component 
15161518        // template that contributes to the index template resolved settings 
1517-         if  (isGlobalAndHasIndexHiddenSetting (projectMetadata ,  winner , winnerName )) {
1519+         if  (isGlobalAndHasIndexHiddenSetting (winner , projectMetadata . componentTemplates () )) {
15181520            throw  new  IllegalStateException (
15191521                "global index template [" 
15201522                    + winnerName 
@@ -1586,12 +1588,11 @@ private static boolean areTemplatesSorted(Collection<Map.Entry<String, Composabl
15861588    // Checks if a global template specifies the `index.hidden` setting. This check is important because a global 
15871589    // template shouldn't specify the `index.hidden` setting, we leave it up to the caller to handle this situation. 
15881590    private  static  boolean  isGlobalAndHasIndexHiddenSetting (
1589-         ProjectMetadata  projectMetadata ,
15901591        ComposableIndexTemplate  template ,
1591-         String   templateName 
1592+         Map < String ,  ComponentTemplate >  componentTemplates 
15921593    ) {
15931594        return  anyMatch (template .indexPatterns (), Regex ::isMatchAllPattern )
1594-             && IndexMetadata .INDEX_HIDDEN_SETTING .exists (resolveSettings (projectMetadata ,  templateName ));
1595+             && IndexMetadata .INDEX_HIDDEN_SETTING .exists (resolveSettings (template ,  componentTemplates ));
15951596    }
15961597
15971598    /** 
@@ -1962,10 +1963,8 @@ private static void validateCompositeTemplate(
19621963        final  NamedXContentRegistry  xContentRegistry ,
19631964        final  SystemIndices  systemIndices 
19641965    ) throws  Exception  {
1965-         final  ProjectMetadata  projectMetadataWithTemplate  = ProjectMetadata .builder (project ).put (templateName , template ).build ();
1966- 
19671966        final  String  temporaryIndexName  = "validate-template-"  + UUIDs .randomBase64UUID ().toLowerCase (Locale .ROOT );
1968-         Settings  resolvedSettings  = resolveSettings (projectMetadataWithTemplate ,  templateName );
1967+         Settings  resolvedSettings  = resolveSettings (template ,  project . componentTemplates () );
19691968
19701969        // use the provided values, otherwise just pick valid dummy values 
19711970        int  dummyPartitionSize  = IndexMetadata .INDEX_ROUTING_PARTITION_SIZE_SETTING .get (resolvedSettings );
@@ -1985,23 +1984,17 @@ private static void validateCompositeTemplate(
19851984            .build ();
19861985
19871986        // Validate index metadata (settings) 
1988-         final  ProjectMetadata  projectMetadataWithIndex  = ProjectMetadata .builder (projectMetadataWithTemplate )
1989-             .put (
1990-                 IndexMetadata .builder (temporaryIndexName )
1991-                     // necessary to pass asserts in ClusterState constructor 
1992-                     .eventIngestedRange (IndexLongFieldRange .UNKNOWN )
1993-                     .settings (finalResolvedSettings )
1994-                     .putCustom (customMetadata )
1995-             )
1987+         final  IndexMetadata  tmpIndexMetadata  = IndexMetadata .builder (temporaryIndexName )
1988+             .settings (finalResolvedSettings )
1989+             .putCustom (customMetadata )
19961990            .build ();
1997-         final  IndexMetadata  tmpIndexMetadata  = projectMetadataWithIndex .index (temporaryIndexName );
19981991        indicesService .withTempIndexService (tmpIndexMetadata , tempIndexService  -> {
19991992            // Validate aliases 
20001993            MetadataCreateIndexService .resolveAndValidateAliases (
20011994                temporaryIndexName ,
20021995                Collections .emptySet (),
2003-                 MetadataIndexTemplateService .resolveAliases (projectMetadataWithIndex ,  templateName ),
2004-                 projectMetadataWithIndex ,
1996+                 MetadataIndexTemplateService .resolveAliases (project ,  template ),
1997+                 project ,
20051998                // the context is only used for validation so it's fine to pass fake values for the 
20061999                // shard id and the current timestamp 
20072000                xContentRegistry ,
@@ -2014,7 +2007,7 @@ private static void validateCompositeTemplate(
20142007            String  indexName  = DataStream .BACKING_INDEX_PREFIX  + temporaryIndexName ;
20152008            // Parse mappings to ensure they are valid after being composed 
20162009
2017-             List <CompressedXContent > mappings  = collectMappings (projectMetadataWithIndex ,  templateName , indexName );
2010+             List <CompressedXContent > mappings  = collectMappings (template ,  project . componentTemplates () , indexName );
20182011            try  {
20192012                MapperService  mapperService  = tempIndexService .mapperService ();
20202013                mapperService .merge (MapperService .SINGLE_MAPPING_NAME , mappings , MapperService .MergeReason .INDEX_TEMPLATE );
@@ -2061,10 +2054,6 @@ public static void validateTemplate(Settings validateSettings, CompressedXConten
20612054        });
20622055    }
20632056
2064-     public  void  validate (String  name , ComponentTemplate  template ) {
2065-         validate (name , template .template (), Collections .emptyList (), null );
2066-     }
2067- 
20682057    private  void  validate (String  name , ComposableIndexTemplate  template , @ Nullable  Settings  systemProvided ) {
20692058        validate (name , template .template (), template .indexPatterns (), systemProvided );
20702059    }
0 commit comments