|
25 | 25 | import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction; |
26 | 26 | import org.elasticsearch.action.bulk.BulkRequest; |
27 | 27 | import org.elasticsearch.action.bulk.BulkResponse; |
| 28 | +import org.elasticsearch.action.datastreams.CreateDataStreamAction; |
| 29 | +import org.elasticsearch.action.datastreams.GetDataStreamAction; |
28 | 30 | import org.elasticsearch.action.index.IndexRequest; |
29 | 31 | import org.elasticsearch.action.ingest.DeletePipelineRequest; |
30 | 32 | import org.elasticsearch.action.ingest.DeletePipelineTransportAction; |
31 | 33 | import org.elasticsearch.action.ingest.GetPipelineAction; |
32 | 34 | import org.elasticsearch.action.ingest.GetPipelineRequest; |
33 | 35 | import org.elasticsearch.action.ingest.PutPipelineRequest; |
34 | 36 | import org.elasticsearch.action.ingest.PutPipelineTransportAction; |
| 37 | +import org.elasticsearch.action.support.IndicesOptions; |
| 38 | +import org.elasticsearch.action.support.master.AcknowledgedRequest; |
35 | 39 | import org.elasticsearch.cluster.block.ClusterBlockException; |
36 | 40 | import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; |
37 | 41 | import org.elasticsearch.cluster.metadata.IndexMetadata; |
|
45 | 49 | import org.elasticsearch.common.time.DateFormatter; |
46 | 50 | import org.elasticsearch.common.time.FormatNames; |
47 | 51 | import org.elasticsearch.common.xcontent.support.XContentMapValues; |
| 52 | +import org.elasticsearch.core.TimeValue; |
48 | 53 | import org.elasticsearch.datastreams.DataStreamsPlugin; |
| 54 | +import org.elasticsearch.index.Index; |
49 | 55 | import org.elasticsearch.index.IndexSettings; |
50 | 56 | import org.elasticsearch.index.engine.frozen.FrozenEngine; |
51 | 57 | import org.elasticsearch.index.mapper.DateFieldMapper; |
|
58 | 64 | import org.elasticsearch.test.ESIntegTestCase; |
59 | 65 | import org.elasticsearch.test.transport.MockTransportService; |
60 | 66 | import org.elasticsearch.xcontent.XContentType; |
| 67 | +import org.elasticsearch.xcontent.json.JsonXContent; |
| 68 | +import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; |
61 | 69 | import org.elasticsearch.xpack.core.frozen.action.FreezeIndexAction; |
| 70 | +import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; |
| 71 | +import org.elasticsearch.xpack.core.ilm.LifecycleSettings; |
| 72 | +import org.elasticsearch.xpack.core.ilm.OperationMode; |
| 73 | +import org.elasticsearch.xpack.core.ilm.Phase; |
| 74 | +import org.elasticsearch.xpack.core.ilm.StartILMRequest; |
| 75 | +import org.elasticsearch.xpack.core.ilm.StopILMRequest; |
| 76 | +import org.elasticsearch.xpack.core.ilm.action.GetStatusAction; |
| 77 | +import org.elasticsearch.xpack.core.ilm.action.ILMActions; |
| 78 | +import org.elasticsearch.xpack.core.ilm.action.PutLifecycleRequest; |
62 | 79 | import org.elasticsearch.xpack.frozen.action.TransportFreezeIndexAction; |
| 80 | +import org.elasticsearch.xpack.ilm.IndexLifecycle; |
63 | 81 | import org.elasticsearch.xpack.migrate.MigratePlugin; |
64 | 82 | import org.elasticsearch.xpack.migrate.MigrateTemplateRegistry; |
65 | 83 | import org.junit.Before; |
|
72 | 90 | import java.util.List; |
73 | 91 | import java.util.Locale; |
74 | 92 | import java.util.Map; |
| 93 | +import java.util.concurrent.TimeUnit; |
75 | 94 |
|
76 | 95 | import static java.lang.Boolean.parseBoolean; |
77 | 96 | import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.DEFAULT_TIMESTAMP_FIELD; |
@@ -125,10 +144,22 @@ protected Collection<Class<? extends Plugin>> nodePlugins() { |
125 | 144 | MockTransportService.TestPlugin.class, |
126 | 145 | DataStreamsPlugin.class, |
127 | 146 | IngestCommonPlugin.class, |
128 | | - TestFrozenIndicesPlugin.class |
| 147 | + TestFrozenIndicesPlugin.class, |
| 148 | + IndexLifecycle.class, |
| 149 | + LocalStateCompositeXPackPlugin.class |
129 | 150 | ); |
130 | 151 | } |
131 | 152 |
|
| 153 | + @Override |
| 154 | + protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { |
| 155 | + return Settings.builder() |
| 156 | + .put(super.nodeSettings(nodeOrdinal, otherSettings)) |
| 157 | + .put(LifecycleSettings.LIFECYCLE_POLL_INTERVAL, "1s") |
| 158 | + // This just generates less churn and makes it easier to read the log file if needed |
| 159 | + .put(LifecycleSettings.LIFECYCLE_HISTORY_INDEX_ENABLED, false) |
| 160 | + .build(); |
| 161 | + } |
| 162 | + |
132 | 163 | private static String DATA_STREAM_MAPPING = """ |
133 | 164 | { |
134 | 165 | "dynamic": true, |
@@ -645,6 +676,133 @@ public void testIndexUnfrozen() { |
645 | 676 | ); |
646 | 677 | } |
647 | 678 |
|
| 679 | + public void testIndexLifecycleSettingNotCopied() throws Exception { |
| 680 | + Map<String, Phase> phases = Map.of( |
| 681 | + "hot", |
| 682 | + new Phase( |
| 683 | + "hot", |
| 684 | + TimeValue.ZERO, |
| 685 | + Map.of( |
| 686 | + "rollover", |
| 687 | + new org.elasticsearch.xpack.core.ilm.RolloverAction(null, null, null, 1L, null, null, null, null, null, null) |
| 688 | + ) |
| 689 | + ) |
| 690 | + ); |
| 691 | + |
| 692 | + var policyName = "my-policy"; |
| 693 | + LifecyclePolicy policy = new LifecyclePolicy(policyName, phases); |
| 694 | + PutLifecycleRequest putLifecycleRequest = new PutLifecycleRequest(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT, policy); |
| 695 | + assertAcked(client().execute(ILMActions.PUT, putLifecycleRequest).actionGet()); |
| 696 | + |
| 697 | + // create data stream with a document and wait for ILM to roll it over |
| 698 | + var dataStream = createDataStream(policyName); |
| 699 | + createDocument(dataStream); |
| 700 | + |
| 701 | + assertAcked(safeGet(client().execute(ILMActions.START, new StartILMRequest(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT)))); |
| 702 | + assertBusy(() -> { |
| 703 | + var getIndexResponse = safeGet(indicesAdmin().getIndex(new GetIndexRequest().indices(dataStream))); |
| 704 | + assertTrue(getIndexResponse.indices().length >= 2); |
| 705 | + }); |
| 706 | + stopILM(); |
| 707 | + |
| 708 | + var dataStreams = safeGet( |
| 709 | + indicesAdmin().execute( |
| 710 | + GetDataStreamAction.INSTANCE, |
| 711 | + new GetDataStreamAction.Request(TEST_REQUEST_TIMEOUT, new String[] { dataStream }) |
| 712 | + ) |
| 713 | + ).getDataStreams(); |
| 714 | + |
| 715 | + assertFalse(dataStreams.isEmpty()); |
| 716 | + String writeIndex = dataStreams.get(0).getDataStream().getWriteIndex().getName(); |
| 717 | + List<String> indices = dataStreams.get(0).getDataStream().getIndices().stream().map(Index::getName).toList(); |
| 718 | + assertTrue(indices.size() >= 2); |
| 719 | + |
| 720 | + for (var backingIndex : indices) { |
| 721 | + if (backingIndex.equals(writeIndex) == false) { |
| 722 | + var destIndex = safeGet( |
| 723 | + client().execute(ReindexDataStreamIndexAction.INSTANCE, new ReindexDataStreamIndexAction.Request(backingIndex)) |
| 724 | + ).getDestIndex(); |
| 725 | + var settingsResponse = safeGet(indicesAdmin().getSettings(new GetSettingsRequest().indices(backingIndex, destIndex))); |
| 726 | + assertEquals(policyName, settingsResponse.getSetting(backingIndex, IndexMetadata.LIFECYCLE_NAME)); |
| 727 | + assertNull(settingsResponse.getSetting(destIndex, IndexMetadata.LIFECYCLE_NAME)); |
| 728 | + } |
| 729 | + } |
| 730 | + } |
| 731 | + |
| 732 | + private void stopILM() throws Exception { |
| 733 | + assertAcked(safeGet(client().execute(ILMActions.STOP, new StopILMRequest(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT)))); |
| 734 | + assertBusy(() -> { |
| 735 | + var statusResponse = safeGet( |
| 736 | + client().execute(GetStatusAction.INSTANCE, new AcknowledgedRequest.Plain(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT)) |
| 737 | + ); |
| 738 | + assertEquals(OperationMode.STOPPED, statusResponse.getMode()); |
| 739 | + }); |
| 740 | + } |
| 741 | + |
| 742 | + private String createDataStream(String ilmPolicy) throws Exception { |
| 743 | + String dataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.getDefault()); |
| 744 | + |
| 745 | + Settings settings = ilmPolicy != null ? Settings.builder().put(IndexMetadata.LIFECYCLE_NAME, ilmPolicy).build() : null; |
| 746 | + |
| 747 | + String mapping = """ |
| 748 | + { |
| 749 | + "properties": { |
| 750 | + "@timestamp": { |
| 751 | + "type":"date" |
| 752 | + }, |
| 753 | + "data":{ |
| 754 | + "type":"keyword" |
| 755 | + } |
| 756 | + } |
| 757 | + } |
| 758 | + """; |
| 759 | + Template idxTemplate = new Template(settings, new CompressedXContent(mapping), null); |
| 760 | + |
| 761 | + ComposableIndexTemplate template = ComposableIndexTemplate.builder() |
| 762 | + .indexPatterns(List.of(dataStreamName + "*")) |
| 763 | + .template(idxTemplate) |
| 764 | + .dataStreamTemplate(new ComposableIndexTemplate.DataStreamTemplate(false, false)) |
| 765 | + .build(); |
| 766 | + |
| 767 | + assertAcked( |
| 768 | + client().execute( |
| 769 | + TransportPutComposableIndexTemplateAction.TYPE, |
| 770 | + new TransportPutComposableIndexTemplateAction.Request(dataStreamName + "_template").indexTemplate(template) |
| 771 | + ) |
| 772 | + ); |
| 773 | + assertAcked( |
| 774 | + client().execute( |
| 775 | + CreateDataStreamAction.INSTANCE, |
| 776 | + new CreateDataStreamAction.Request(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT, dataStreamName) |
| 777 | + ) |
| 778 | + ); |
| 779 | + return dataStreamName; |
| 780 | + } |
| 781 | + |
| 782 | + private long createDocument(String dataStreamName) throws Exception { |
| 783 | + // Get some randomized but reasonable timestamps on the data since not all of it is guaranteed to arrive in order. |
| 784 | + long timeSeed = System.currentTimeMillis(); |
| 785 | + long timestamp = randomLongBetween(timeSeed - TimeUnit.HOURS.toMillis(5), timeSeed); |
| 786 | + safeGet( |
| 787 | + client().index( |
| 788 | + new IndexRequest(dataStreamName).opType(DocWriteRequest.OpType.CREATE) |
| 789 | + .source( |
| 790 | + JsonXContent.contentBuilder() |
| 791 | + .startObject() |
| 792 | + .field("@timestamp", timestamp) |
| 793 | + .field("data", randomAlphaOfLength(25)) |
| 794 | + .endObject() |
| 795 | + ) |
| 796 | + ) |
| 797 | + ); |
| 798 | + safeGet( |
| 799 | + indicesAdmin().refresh( |
| 800 | + new RefreshRequest(".ds-" + dataStreamName + "*").indicesOptions(IndicesOptions.lenientExpandOpenHidden()) |
| 801 | + ) |
| 802 | + ); |
| 803 | + return timestamp; |
| 804 | + } |
| 805 | + |
648 | 806 | private static void cleanupMetadataBlocks(String index) { |
649 | 807 | var settings = Settings.builder() |
650 | 808 | .putNull(IndexMetadata.SETTING_READ_ONLY) |
|
0 commit comments