Skip to content

Commit d34c2f7

Browse files
[9.2] Add datastream lifecycle support to indices metadata (#245548) (#245567)
# Backport This will backport the following commits from `main` to `9.2`: - [Add datastream lifecycle support to indices metadata (#245548)](#245548) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Sebastián Zaffarano","email":"sebastian.zaffarano@elastic.co"},"sourceCommit":{"committedDate":"2025-12-08T19:06:30Z","message":"Add datastream lifecycle support to indices metadata (#245548)\n\n## Summary\n\nAdd [Datastream life\ncycle](https://www.elastic.co/docs/manage-data/lifecycle/data-stream)\nsupport to the indices metadata plugin.\n\nFor data streams using DSL, the plugin now also queries the\n`data_retention` and includes it in the EBT document.\n\nExample document returned by `GET _data_stream/<ds\nname>/?filter_path=data_streams.name,data_streams.indices,data_streams.lifecycle.enabled,data_streams.lifecycle.data_retention`\n\n```json\n{\n \"data_streams\": [\n {\n \"name\": \"dsl-test\",\n \"indices\": [\n {\n \"index_name\": \".ds-dsl-test-2025.12.08-000001\",\n \"index_uuid\": \"h9nu5fEIQJ-ObVemiXTPqg\",\n \"managed_by\": \"Data stream lifecycle\",\n \"prefer_ilm\": true,\n \"index_mode\": \"standard\"\n },\n {\n \"index_name\": \".ds-dsl-test-2025.12.08-000002\",\n \"index_uuid\": \"VKp5OURcTIquxTmatmNz3g\",\n \"managed_by\": \"Data stream lifecycle\",\n \"prefer_ilm\": true,\n \"index_mode\": \"standard\"\n }\n ],\n \"lifecycle\": {\n \"enabled\": true,\n \"data_retention\": \"1h\"\n }\n }\n ]\n}\n```\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [ ] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [ ] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n- [ ] Review the [backport\nguidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)\nand apply applicable `backport:*` labels.","sha":"f6550e8b686814584a6b48afa28920171ecb44ba","branchLabelMapping":{"^v9.3.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team: SecuritySolution","backport:version","v9.3.0","v9.2.3","v9.1.9"],"title":"Add datastream lifecycle support to indices metadata","number":245548,"url":"https://github.com/elastic/kibana/pull/245548","mergeCommit":{"message":"Add datastream lifecycle support to indices metadata (#245548)\n\n## Summary\n\nAdd [Datastream life\ncycle](https://www.elastic.co/docs/manage-data/lifecycle/data-stream)\nsupport to the indices metadata plugin.\n\nFor data streams using DSL, the plugin now also queries the\n`data_retention` and includes it in the EBT document.\n\nExample document returned by `GET _data_stream/<ds\nname>/?filter_path=data_streams.name,data_streams.indices,data_streams.lifecycle.enabled,data_streams.lifecycle.data_retention`\n\n```json\n{\n \"data_streams\": [\n {\n \"name\": \"dsl-test\",\n \"indices\": [\n {\n \"index_name\": \".ds-dsl-test-2025.12.08-000001\",\n \"index_uuid\": \"h9nu5fEIQJ-ObVemiXTPqg\",\n \"managed_by\": \"Data stream lifecycle\",\n \"prefer_ilm\": true,\n \"index_mode\": \"standard\"\n },\n {\n \"index_name\": \".ds-dsl-test-2025.12.08-000002\",\n \"index_uuid\": \"VKp5OURcTIquxTmatmNz3g\",\n \"managed_by\": \"Data stream lifecycle\",\n \"prefer_ilm\": true,\n \"index_mode\": \"standard\"\n }\n ],\n \"lifecycle\": {\n \"enabled\": true,\n \"data_retention\": \"1h\"\n }\n }\n ]\n}\n```\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [ ] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [ ] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n- [ ] Review the [backport\nguidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)\nand apply applicable `backport:*` labels.","sha":"f6550e8b686814584a6b48afa28920171ecb44ba"}},"sourceBranch":"main","suggestedTargetBranches":["9.2","9.1"],"targetPullRequestStates":[{"branch":"main","label":"v9.3.0","branchLabelMappingKey":"^v9.3.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/245548","number":245548,"mergeCommit":{"message":"Add datastream lifecycle support to indices metadata (#245548)\n\n## Summary\n\nAdd [Datastream life\ncycle](https://www.elastic.co/docs/manage-data/lifecycle/data-stream)\nsupport to the indices metadata plugin.\n\nFor data streams using DSL, the plugin now also queries the\n`data_retention` and includes it in the EBT document.\n\nExample document returned by `GET _data_stream/<ds\nname>/?filter_path=data_streams.name,data_streams.indices,data_streams.lifecycle.enabled,data_streams.lifecycle.data_retention`\n\n```json\n{\n \"data_streams\": [\n {\n \"name\": \"dsl-test\",\n \"indices\": [\n {\n \"index_name\": \".ds-dsl-test-2025.12.08-000001\",\n \"index_uuid\": \"h9nu5fEIQJ-ObVemiXTPqg\",\n \"managed_by\": \"Data stream lifecycle\",\n \"prefer_ilm\": true,\n \"index_mode\": \"standard\"\n },\n {\n \"index_name\": \".ds-dsl-test-2025.12.08-000002\",\n \"index_uuid\": \"VKp5OURcTIquxTmatmNz3g\",\n \"managed_by\": \"Data stream lifecycle\",\n \"prefer_ilm\": true,\n \"index_mode\": \"standard\"\n }\n ],\n \"lifecycle\": {\n \"enabled\": true,\n \"data_retention\": \"1h\"\n }\n }\n ]\n}\n```\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [ ] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [ ] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [ ] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n- [ ] Review the [backport\nguidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)\nand apply applicable `backport:*` labels.","sha":"f6550e8b686814584a6b48afa28920171ecb44ba"}},{"branch":"9.2","label":"v9.2.3","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.1","label":"v9.1.9","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Sebastián Zaffarano <sebastian.zaffarano@elastic.co>
1 parent ef1591e commit d34c2f7

6 files changed

Lines changed: 483 additions & 2 deletions

File tree

x-pack/platform/plugins/private/indices_metadata/server/lib/ebt/events.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ export const DATA_STREAM_EVENT: EventTypeOpts<DataStreams> = {
3131
type: 'keyword',
3232
_meta: { optional: true, description: 'ILM policy associated to the datastream' },
3333
},
34+
dsl: {
35+
properties: {
36+
enabled: {
37+
type: 'boolean',
38+
_meta: { description: 'Whether the data stream is enabled' },
39+
},
40+
data_retention: {
41+
type: 'text',
42+
_meta: { optional: true, description: 'Data retention period' },
43+
},
44+
},
45+
_meta: { optional: true, description: 'Data stream lifecycle settings' },
46+
},
3447
template: {
3548
type: 'keyword',
3649
_meta: { optional: true, description: 'Template associated to the datastream' },

x-pack/platform/plugins/private/indices_metadata/server/lib/services/indices_metadata.test.ts

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ describe('Indices Metadata - IndicesMetadataService', () => {
7171
const mockDataStreams: DataStream[] = [
7272
{
7373
datastream_name: 'test-datastream',
74+
dsl: {
75+
enabled: true,
76+
data_retention: '30d',
77+
},
7478
indices: [{ index_name: 'test-index-1', ilm_policy: 'policy1' }],
7579
},
7680
];
@@ -365,6 +369,60 @@ describe('Indices Metadata - IndicesMetadataService', () => {
365369
expect(receiver.getIlmsStats).toHaveBeenCalledWith(['test-index-1']);
366370
});
367371

372+
it('should publish datastreams with DSL through full metadata flow', async () => {
373+
const datastreamsWithDsl: DataStream[] = [
374+
{
375+
datastream_name: 'logs-test',
376+
dsl: {
377+
enabled: true,
378+
data_retention: '90d',
379+
},
380+
indices: [
381+
{
382+
index_name: '.ds-logs-test-000001',
383+
ilm_policy: 'logs-policy',
384+
},
385+
],
386+
},
387+
];
388+
389+
receiver.getIndices.mockResolvedValue(mockIndexSettings);
390+
receiver.getDataStreams.mockResolvedValue(datastreamsWithDsl);
391+
receiver.getIndexTemplatesStats.mockResolvedValue(mockIndexTemplates);
392+
receiver.getIndicesStats.mockImplementation(async function* () {
393+
yield* mockIndexStats;
394+
});
395+
receiver.isIlmStatsAvailable.mockResolvedValue(true);
396+
receiver.getIlmsStats.mockImplementation(async function* () {
397+
yield {
398+
index_name: '.ds-logs-test-000001',
399+
phase: 'hot',
400+
age: '1d',
401+
policy_name: 'logs-policy',
402+
};
403+
});
404+
receiver.getIlmsPolicies.mockImplementation(async function* () {
405+
yield { policy_name: 'logs-policy', modified_date: '2023-01-01', phases: {} };
406+
});
407+
408+
await service['publishIndicesMetadata'](); // eslint-disable-line dot-notation
409+
410+
expect(sender.reportEBT).toHaveBeenCalledWith(
411+
expect.objectContaining({ eventType: DATA_STREAM_EVENT.eventType }),
412+
{
413+
items: expect.arrayContaining([
414+
expect.objectContaining({
415+
datastream_name: 'logs-test',
416+
dsl: {
417+
enabled: true,
418+
data_retention: '90d',
419+
},
420+
}),
421+
]),
422+
}
423+
);
424+
});
425+
368426
it('should throw error when not initialized', async () => {
369427
const uninitializedService = new IndicesMetadataService(logger, configurationService);
370428

@@ -441,6 +499,116 @@ describe('Indices Metadata - IndicesMetadataService', () => {
441499
expect(result).toBe(1);
442500
expect(logger.debug).toHaveBeenCalledWith('Data streams events sent', { count: 1 });
443501
});
502+
503+
it('should publish datastreams with DSL enabled and retention', () => {
504+
const datastreamsWithDsl: DataStream[] = [
505+
{
506+
datastream_name: 'logs-app-prod',
507+
dsl: {
508+
enabled: true,
509+
data_retention: '7d',
510+
},
511+
indices: [{ index_name: '.ds-logs-app-prod-000001' }],
512+
},
513+
{
514+
datastream_name: 'metrics-system',
515+
dsl: {
516+
enabled: false,
517+
data_retention: undefined,
518+
},
519+
indices: [{ index_name: '.ds-metrics-system-000001' }],
520+
},
521+
];
522+
523+
const result = service['publishDatastreamsStats'](datastreamsWithDsl); // eslint-disable-line dot-notation
524+
525+
expect(sender.reportEBT).toHaveBeenCalledWith(
526+
expect.objectContaining({ eventType: DATA_STREAM_EVENT.eventType }),
527+
{
528+
items: [
529+
expect.objectContaining({
530+
datastream_name: 'logs-app-prod',
531+
dsl: {
532+
enabled: true,
533+
data_retention: '7d',
534+
},
535+
}),
536+
expect.objectContaining({
537+
datastream_name: 'metrics-system',
538+
dsl: {
539+
enabled: false,
540+
data_retention: undefined,
541+
},
542+
}),
543+
],
544+
}
545+
);
546+
expect(result).toBe(2);
547+
});
548+
549+
it('should handle datastreams without DSL field', () => {
550+
const datastreamsWithoutDsl: DataStream[] = [
551+
{
552+
datastream_name: 'legacy-datastream',
553+
indices: [{ index_name: '.ds-legacy-000001' }],
554+
},
555+
];
556+
557+
const result = service['publishDatastreamsStats'](datastreamsWithoutDsl); // eslint-disable-line dot-notation
558+
559+
expect(sender.reportEBT).toHaveBeenCalledWith(
560+
expect.objectContaining({ eventType: DATA_STREAM_EVENT.eventType }),
561+
{
562+
items: [
563+
{
564+
datastream_name: 'legacy-datastream',
565+
indices: [{ index_name: '.ds-legacy-000001' }],
566+
},
567+
],
568+
}
569+
);
570+
expect(result).toBe(1);
571+
});
572+
573+
it('should publish mixed DSL configurations', () => {
574+
const mixedDatastreams: DataStream[] = [
575+
{
576+
datastream_name: 'with-retention',
577+
dsl: { enabled: true, data_retention: '365d' },
578+
indices: [],
579+
},
580+
{
581+
datastream_name: 'enabled-no-retention',
582+
dsl: { enabled: true, data_retention: undefined },
583+
indices: [],
584+
},
585+
{
586+
datastream_name: 'disabled',
587+
dsl: { enabled: false, data_retention: undefined },
588+
indices: [],
589+
},
590+
];
591+
592+
const result = service['publishDatastreamsStats'](mixedDatastreams); // eslint-disable-line dot-notation
593+
594+
expect(sender.reportEBT).toHaveBeenCalledWith(
595+
expect.objectContaining({ eventType: DATA_STREAM_EVENT.eventType }),
596+
{
597+
items: expect.arrayContaining([
598+
expect.objectContaining({
599+
dsl: { enabled: true, data_retention: '365d' },
600+
}),
601+
expect.objectContaining({
602+
dsl: { enabled: true, data_retention: undefined },
603+
}),
604+
expect.objectContaining({
605+
dsl: { enabled: false, data_retention: undefined },
606+
}),
607+
]),
608+
}
609+
);
610+
expect(result).toBe(3);
611+
});
444612
});
445613

446614
describe('publishIndicesSettings', () => {

x-pack/platform/plugins/private/indices_metadata/server/lib/services/indices_metadata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ export class IndicesMetadataService {
257257
this.logger.debug('Indices settings sent', { count: indicesSettings.items.length } as LogMeta);
258258
return indicesSettings.items.length;
259259
}
260+
260261
private async publishIlmStats(indices: string[]): Promise<Set<string>> {
261262
const ilmNames = new Set<string>();
262263
const ilmsStats: IlmsStats = {

x-pack/platform/plugins/private/indices_metadata/server/lib/services/indices_metadata.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,16 @@ export interface Index {
114114
export interface DataStreams {
115115
items: DataStream[];
116116
}
117+
118+
export interface DataStreamLifeCycle {
119+
enabled: boolean;
120+
data_retention?: string;
121+
}
122+
117123
export interface DataStream {
118124
datastream_name: string;
119125
ilm_policy?: string;
126+
dsl?: DataStreamLifeCycle;
120127
template?: string;
121128
indices?: Index[];
122129
}

0 commit comments

Comments
 (0)