1010import org .elasticsearch .ElasticsearchException ;
1111import org .elasticsearch .action .admin .indices .create .CreateIndexRequest ;
1212import org .elasticsearch .action .admin .indices .delete .DeleteIndexRequest ;
13+ import org .elasticsearch .action .admin .indices .rollover .RolloverResponse ;
14+ import org .elasticsearch .action .admin .indices .stats .IndicesStatsResponse ;
15+ import org .elasticsearch .action .admin .indices .template .put .PutComposableIndexTemplateAction ;
16+ import org .elasticsearch .action .datastreams .CreateDataStreamAction ;
17+ import org .elasticsearch .action .datastreams .ModifyDataStreamsAction ;
1318import org .elasticsearch .client .internal .Client ;
19+ import org .elasticsearch .cluster .metadata .ComposableIndexTemplate ;
20+ import org .elasticsearch .cluster .metadata .DataStream ;
21+ import org .elasticsearch .cluster .metadata .DataStreamAction ;
1422import org .elasticsearch .cluster .metadata .IndexMetadata ;
1523import org .elasticsearch .cluster .metadata .Metadata ;
24+ import org .elasticsearch .cluster .metadata .MetadataIndexTemplateService ;
25+ import org .elasticsearch .cluster .metadata .Template ;
1626import org .elasticsearch .common .Strings ;
1727import org .elasticsearch .common .regex .Regex ;
1828import org .elasticsearch .common .settings .Settings ;
1929import org .elasticsearch .common .unit .ByteSizeUnit ;
2030import org .elasticsearch .common .unit .ByteSizeValue ;
2131import org .elasticsearch .core .CheckedRunnable ;
2232import org .elasticsearch .core .TimeValue ;
33+ import org .elasticsearch .datastreams .DataStreamsPlugin ;
2334import org .elasticsearch .index .IndexNotFoundException ;
2435import org .elasticsearch .indices .SystemIndexDescriptor ;
2536import org .elasticsearch .plugins .Plugin ;
5162import java .util .stream .Stream ;
5263
5364import static org .elasticsearch .test .hamcrest .ElasticsearchAssertions .assertAcked ;
65+ import static org .hamcrest .Matchers .aMapWithSize ;
5466import static org .hamcrest .Matchers .equalTo ;
5567import static org .hamcrest .Matchers .greaterThanOrEqualTo ;
68+ import static org .hamcrest .Matchers .hasKey ;
5669import static org .hamcrest .Matchers .hasSize ;
5770import static org .hamcrest .Matchers .is ;
5871import static org .hamcrest .Matchers .notNullValue ;
@@ -67,7 +80,7 @@ protected boolean reuseClusters() {
6780
6881 @ Override
6982 protected Collection <Class <? extends Plugin >> nodePlugins () {
70- return Stream .concat (super .nodePlugins ().stream (), Stream .of (FakeSystemIndex .class )).collect ( Collectors . toList () );
83+ return Stream .concat (super .nodePlugins ().stream (), Stream .of (FakeSystemIndex .class , DataStreamsPlugin . class )).toList ();
7184 }
7285
7386 public static class FakeSystemIndex extends Plugin implements SystemIndexPlugin {
@@ -621,6 +634,98 @@ public void testAutoFollowExclusion() throws Exception {
621634 assertFalse (ESIntegTestCase .indexExists ("copy-logs-201801" , followerClient ()));
622635 }
623636
637+ public void testAutoFollowDatastreamWithClosingFollowerIndex () throws Exception {
638+ final String datastream = "logs-1" ;
639+ PutComposableIndexTemplateAction .Request request = new PutComposableIndexTemplateAction .Request ("template-id" );
640+ request .indexTemplate (
641+ new ComposableIndexTemplate (
642+ List .of ("logs-*" ),
643+ new Template (
644+ Settings .builder ()
645+ .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , 1 )
646+ .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 0 )
647+ .build (),
648+ null ,
649+ null
650+ ),
651+ null ,
652+ null ,
653+ null ,
654+ null ,
655+ new ComposableIndexTemplate .DataStreamTemplate (),
656+ null
657+ )
658+ );
659+ assertAcked (leaderClient ().execute (PutComposableIndexTemplateAction .INSTANCE , request ).get ());
660+
661+ CreateDataStreamAction .Request createDataStreamRequest = new CreateDataStreamAction .Request (datastream );
662+ assertAcked (leaderClient ().execute (CreateDataStreamAction .INSTANCE , createDataStreamRequest ).get ());
663+ leaderClient ().prepareIndex (datastream )
664+ .setCreate (true )
665+ .setSource ("foo" , "bar" , DataStream .TIMESTAMP_FIELD .getName (), randomNonNegativeLong ())
666+ .get ();
667+
668+ PutAutoFollowPatternAction .Request followRequest = new PutAutoFollowPatternAction .Request ();
669+ followRequest .setName ("pattern-1" );
670+ followRequest .setRemoteCluster ("leader_cluster" );
671+ followRequest .setLeaderIndexPatterns (List .of ("logs-*" ));
672+ followRequest .setFollowIndexNamePattern ("{{leader_index}}" );
673+ assertTrue (followerClient ().execute (PutAutoFollowPatternAction .INSTANCE , followRequest ).get ().isAcknowledged ());
674+
675+ logger .info ("--> roll over once and wait for the auto-follow to pick up the new index" );
676+ leaderClient ().admin ().indices ().prepareRolloverIndex ("logs-1" ).get ();
677+ assertLongBusy (() -> {
678+ AutoFollowStats autoFollowStats = getAutoFollowStats ();
679+ assertThat (autoFollowStats .getNumberOfSuccessfulFollowIndices (), equalTo (1L ));
680+ });
681+
682+ ensureFollowerGreen ("*" );
683+
684+ final RolloverResponse rolloverResponse = leaderClient ().admin ().indices ().prepareRolloverIndex (datastream ).get ();
685+ final String indexInDatastream = rolloverResponse .getOldIndex ();
686+
687+ logger .info ("--> closing [{}] on follower so it will be re-opened by crr" , indexInDatastream );
688+ assertAcked (followerClient ().admin ().indices ().prepareClose (indexInDatastream ).setMasterNodeTimeout (TimeValue .MAX_VALUE ).get ());
689+
690+ logger .info ("--> deleting and recreating index [{}] on leader to change index uuid on leader" , indexInDatastream );
691+ assertAcked (leaderClient ().admin ().indices ().prepareDelete (indexInDatastream ).get ());
692+ assertAcked (
693+ leaderClient ().admin ()
694+ .indices ()
695+ .prepareCreate (indexInDatastream )
696+ .setMapping (MetadataIndexTemplateService .DEFAULT_TIMESTAMP_MAPPING .toString ())
697+ .get ()
698+ );
699+ leaderClient ().prepareIndex (indexInDatastream )
700+ .setCreate (true )
701+ .setSource ("foo" , "bar" , DataStream .TIMESTAMP_FIELD .getName (), randomNonNegativeLong ())
702+ .get ();
703+ leaderClient ().execute (
704+ ModifyDataStreamsAction .INSTANCE ,
705+ new ModifyDataStreamsAction .Request (List .of (DataStreamAction .addBackingIndex (datastream , indexInDatastream )))
706+ ).get ();
707+
708+ assertLongBusy (() -> {
709+ AutoFollowStats autoFollowStats = getAutoFollowStats ();
710+ assertThat (autoFollowStats .getNumberOfSuccessfulFollowIndices (), equalTo (3L ));
711+ });
712+
713+ final Metadata metadata = followerClient ().admin ().cluster ().prepareState ().get ().getState ().metadata ();
714+ final DataStream dataStream = metadata .dataStreams ().get (datastream );
715+ assertTrue (dataStream .getIndices ().stream ().anyMatch (i -> i .getName ().equals (indexInDatastream )));
716+ assertEquals (IndexMetadata .State .OPEN , metadata .index (indexInDatastream ).getState ());
717+ ensureFollowerGreen ("*" );
718+ final IndicesStatsResponse stats = followerClient ().admin ().indices ().prepareStats (datastream ).get ();
719+ assertThat (stats .getIndices (), aMapWithSize (2 ));
720+
721+ assertAcked (leaderClient ().admin ().indices ().prepareDelete (indexInDatastream ).get ());
722+ assertAcked (followerClient ().admin ().indices ().prepareDelete (indexInDatastream ).setMasterNodeTimeout (TimeValue .MAX_VALUE ).get ());
723+ ensureFollowerGreen ("*" );
724+ final IndicesStatsResponse statsAfterDelete = followerClient ().admin ().indices ().prepareStats (datastream ).get ();
725+ assertThat (statsAfterDelete .getIndices (), aMapWithSize (1 ));
726+ assertThat (statsAfterDelete .getIndices (), hasKey (rolloverResponse .getNewIndex ()));
727+ }
728+
624729 private void putAutoFollowPatterns (String name , String [] patterns ) {
625730 putAutoFollowPatterns (name , patterns , Collections .emptyList ());
626731 }
0 commit comments