Skip to content

Commit 253000b

Browse files
[9.1] Add datastream lifecycle support to indices metadata (#245548) (#245566)
# Backport This will backport the following commits from `main` to `9.1`: - [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":"[email protected]"},"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 <[email protected]>
1 parent 7ef375f commit 253000b

File tree

6 files changed

+483
-2
lines changed

6 files changed

+483
-2
lines changed

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

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

443611
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
@@ -113,9 +113,16 @@ export interface Index {
113113
export interface DataStreams {
114114
items: DataStream[];
115115
}
116+
117+
export interface DataStreamLifeCycle {
118+
enabled: boolean;
119+
data_retention?: string;
120+
}
121+
116122
export interface DataStream {
117123
datastream_name: string;
118124
ilm_policy?: string;
125+
dsl?: DataStreamLifeCycle;
119126
template?: string;
120127
indices?: Index[];
121128
}

0 commit comments

Comments
 (0)