From 8aaad3213824c4e46064c166a42bc043f912a628 Mon Sep 17 00:00:00 2001 From: Elena Stoeva <59341489+ElenaStoeva@users.noreply.github.com> Date: Thu, 31 Oct 2024 09:25:16 +0000 Subject: [PATCH] [Index Management] Add support for index mode (#197874) Closes https://github.com/elastic/kibana/issues/195772 ## Summary This PR displays the index mode setting in Index templates (at Review step in creation flow and at template details flyout) and Data streams (in ds table and ds details flyout). Screenshot 2024-10-28 at 14 39 18 Screenshot 2024-10-28 at 19 05 29 Screenshot 2024-10-28 at 14 41 01 Screenshot 2024-10-28 at 14 41 09 ### How to test: **Creating a Logsdb index template and data stream:** 1. Go to Index Managament -> Index templates and start creating a new template 2. Add a name and an index pattern `test-logsdb` and then go to the Settings step 3. Add the setting `"index.mode": "logsdb`. 4. Go to last step (review) and verify that the index mode is correctly displayed in Summary. Save the template. 5. In the template flyout, verify that the index mode displays the correct label. 6. Go to Console and create a data stream that mathes the index pattern of the created index template: `PUT _data_stream/test-logsdb` 7. Go to Index Management -> Data streams 8. In the data streams table, verify that the index mode column is correct for the new data stream. 9. Open the new data stream and verify that the details flyout displays the correct index mode. **Creating a Time series index template and data stream:** 1. Go to Index Managament -> Index templates and start creating a new template 2. Add a name and an index pattern `test-tsds` and then go to the Settings step 3. Add the setting `"index.mode": "time_series`. 4. For time series index template, we also need to add a mapping with a `time_series_dimension` property. Go to the Mappings step, click on "Load JSON" and add the following mappings object: ``` { "properties": { "id": { "type": "keyword", "time_series_dimension": true } } } ``` 6. Go to last step (review) and verify that the index mode is correctly displayed in Summary. Save the template. 7. In the template flyout, verify that the index mode displays the correct label. 8. Go to Console and create a data stream that mathes the index pattern of the created index template: `PUT _data_stream/test-tsds` 9. Go to Index Management -> Data streams 10. In the data streams table, verify that the index mode column is correct for the new data stream. 11. Open the new data stream and verify that the details flyout displays the correct index mode. ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7288 - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 40ddfbc044c7f9fed5cc39b57ecc7d0c479ea36d) --- .../home/data_streams_tab.helpers.ts | 1 + .../home/data_streams_tab.test.ts | 62 ++++++++++++++----- .../common/lib/template_serialization.ts | 4 +- .../common/types/data_streams.ts | 4 ++ .../index_management/common/types/indices.ts | 33 +--------- .../template_form/steps/step_review.tsx | 14 +++++ .../application/lib/index_mode_labels.ts | 29 +++++++++ .../data_stream_detail_panel.tsx | 13 ++++ .../data_stream_table/data_stream_table.tsx | 11 ++++ .../index_table/index_table_pagination.tsx | 4 +- .../template_details/tabs/tab_summary.tsx | 13 ++++ .../server/lib/data_stream_serialization.ts | 3 + .../api/data_streams/register_get_route.ts | 30 +++++++++ .../hooks/use_get_data_stream_statuses.ts | 1 + .../index_management/data_streams.ts | 19 ++++++ .../lib/datastreams.helpers.ts | 7 ++- .../data_streams_tab/data_streams_tab.ts | 41 ++++++++++++ .../index_management/index_template_wizard.ts | 4 ++ .../reporting_and_security/datastream.ts | 1 + .../common/index_management/datastreams.ts | 2 + 20 files changed, 246 insertions(+), 50 deletions(-) create mode 100644 x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts index bd119a77378af..608d2ce5390da 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts @@ -298,6 +298,7 @@ export const createDataStreamPayload = (dataStream: Partial): DataSt enabled: true, data_retention: '7d', }, + indexMode: 'standard', ...dataStream, }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts index a4ea7b9296e28..1d7ee65790cfd 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts @@ -205,8 +205,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '1', '7 days', 'Delete'], - ['', 'dataStream2', 'green', '1', '5 days ', 'Delete'], + ['', 'dataStream1', 'green', '1', 'Standard', '7 days', 'Delete'], + ['', 'dataStream2', 'green', '1', 'Standard', '5 days ', 'Delete'], ]); }); @@ -254,6 +254,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '5b', '1', + 'Standard', '7 days', 'Delete', ], @@ -264,6 +265,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '1kb', '1', + 'Standard', '5 days ', 'Delete', ], @@ -289,6 +291,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '5b', '1', + 'Standard', '7 days', 'Delete', ], @@ -299,6 +302,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '1kb', '1', + 'Standard', '5 days ', 'Delete', ], @@ -346,8 +350,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '156kb', '10000', '1', '7 days', 'Delete'], - ['', 'dataStream2', 'green', '156kb', '10000', '1', '5 days ', 'Delete'], + ['', 'dataStream1', 'green', '156kb', '10000', '1', 'Standard', '7 days', 'Delete'], + ['', 'dataStream2', 'green', '156kb', '10000', '1', 'Standard', '5 days ', 'Delete'], ]); }); @@ -378,6 +382,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '5b', '1', + 'Standard', '7 days', 'Delete', ], @@ -388,6 +393,7 @@ describe('Data Streams tab', () => { 'December 31st, 1969 7:00:00 PM', '1kb', '1', + 'Standard', '5 days ', 'Delete', ], @@ -509,8 +515,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '1', 'Disabled', 'Delete'], - ['', 'dataStream2', 'green', '1', '', 'Delete'], + ['', 'dataStream1', 'green', '1', 'Standard', 'Disabled', 'Delete'], + ['', 'dataStream2', 'green', '1', 'Standard', '', 'Delete'], ]); await actions.clickNameAt(0); @@ -892,8 +898,16 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `managed-data-stream${nonBreakingSpace}Managed`, 'green', '1', '7 days', 'Delete'], - ['', 'non-managed-data-stream', 'green', '1', '7 days', 'Delete'], + [ + '', + `managed-data-stream${nonBreakingSpace}Managed`, + 'green', + '1', + 'Standard', + '7 days', + 'Delete', + ], + ['', 'non-managed-data-stream', 'green', '1', 'Standard', '7 days', 'Delete'], ]); }); @@ -902,15 +916,23 @@ describe('Data Streams tab', () => { let { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `managed-data-stream${nonBreakingSpace}Managed`, 'green', '1', '7 days', 'Delete'], - ['', 'non-managed-data-stream', 'green', '1', '7 days', 'Delete'], + [ + '', + `managed-data-stream${nonBreakingSpace}Managed`, + 'green', + '1', + 'Standard', + '7 days', + 'Delete', + ], + ['', 'non-managed-data-stream', 'green', '1', 'Standard', '7 days', 'Delete'], ]); actions.toggleViewFilterAt(0); ({ tableCellsValues } = table.getMetaData('dataStreamTable')); expect(tableCellsValues).toEqual([ - ['', 'non-managed-data-stream', 'green', '1', '7 days', 'Delete'], + ['', 'non-managed-data-stream', 'green', '1', 'Standard', '7 days', 'Delete'], ]); }); }); @@ -942,7 +964,15 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `hidden-data-stream${nonBreakingSpace}Hidden`, 'green', '1', '7 days', 'Delete'], + [ + '', + `hidden-data-stream${nonBreakingSpace}Hidden`, + 'green', + '1', + 'Standard', + '7 days', + 'Delete', + ], ]); }); }); @@ -989,10 +1019,10 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStreamNoDelete', 'green', '1', '7 days', ''], - ['', 'dataStreamNoEditRetention', 'green', '1', '7 days', 'Delete'], - ['', 'dataStreamNoPermissions', 'green', '1', '7 days', ''], - ['', 'dataStreamWithDelete', 'green', '1', '7 days', 'Delete'], + ['', 'dataStreamNoDelete', 'green', '1', 'Standard', '7 days', ''], + ['', 'dataStreamNoEditRetention', 'green', '1', 'Standard', '7 days', 'Delete'], + ['', 'dataStreamNoPermissions', 'green', '1', 'Standard', '7 days', ''], + ['', 'dataStreamWithDelete', 'green', '1', 'Standard', '7 days', 'Delete'], ]); }); diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts index f8b4ed47a22f7..0ed52e3f04ba0 100644 --- a/x-pack/plugins/index_management/common/lib/template_serialization.ts +++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts @@ -73,6 +73,8 @@ export function deserializeTemplate( type = 'managed'; } + const ilmPolicyName = settings?.index?.lifecycle?.name; + const deserializedTemplate: TemplateDeserialized = { name, version, @@ -80,7 +82,7 @@ export function deserializeTemplate( ...(template.lifecycle ? { lifecycle: deserializeESLifecycle(template.lifecycle) } : {}), indexPatterns: indexPatterns.sort(), template, - ilmPolicy: settings?.index?.lifecycle, + ilmPolicy: ilmPolicyName ? { name: ilmPolicyName } : undefined, composedOf: composedOf ?? [], ignoreMissingComponentTemplates: ignoreMissingComponentTemplates ?? [], dataStream, diff --git a/x-pack/plugins/index_management/common/types/data_streams.ts b/x-pack/plugins/index_management/common/types/data_streams.ts index 78c671969f579..993d32f32bee1 100644 --- a/x-pack/plugins/index_management/common/types/data_streams.ts +++ b/x-pack/plugins/index_management/common/types/data_streams.ts @@ -33,6 +33,8 @@ export type DataStreamIndexFromEs = IndicesDataStreamIndex; export type Health = 'green' | 'yellow' | 'red'; +export type IndexMode = 'standard' | 'logsdb' | 'time_series'; + export interface EnhancedDataStreamFromEs extends IndicesDataStream { global_max_retention?: string; store_size?: IndicesDataStreamsStatsDataStreamsStatsItem['store_size']; @@ -45,6 +47,7 @@ export interface EnhancedDataStreamFromEs extends IndicesDataStream { delete_index: boolean; manage_data_stream_lifecycle: boolean; }; + index_mode?: string | null; } export interface DataStream { @@ -71,6 +74,7 @@ export interface DataStream { retention_determined_by?: string; globalMaxRetention?: string; }; + indexMode: IndexMode; } export interface DataStreamIndex { diff --git a/x-pack/plugins/index_management/common/types/indices.ts b/x-pack/plugins/index_management/common/types/indices.ts index 612aaf3bd6c9b..804a1bce1e299 100644 --- a/x-pack/plugins/index_management/common/types/indices.ts +++ b/x-pack/plugins/index_management/common/types/indices.ts @@ -5,29 +5,9 @@ * 2.0. */ -export type { Index } from '@kbn/index-management-shared-types'; +import { IndicesIndexSettingsKeys } from '@elastic/elasticsearch/lib/api/types'; -export interface IndexModule { - number_of_shards: number | string; - codec: string; - routing_partition_size: number; - refresh_interval: string; - load_fixed_bitset_filters_eagerly: boolean; - shard: { - check_on_startup: boolean | 'checksum'; - }; - number_of_replicas: number; - auto_expand_replicas: false | string; - lifecycle: LifecycleModule; - routing: { - allocation: { - enable: 'all' | 'primaries' | 'new_primaries' | 'none'; - }; - rebalance: { - enable: 'all' | 'primaries' | 'replicas' | 'none'; - }; - }; -} +export type { Index } from '@kbn/index-management-shared-types'; interface AnalysisModule { analyzer: { @@ -41,15 +21,8 @@ interface AnalysisModule { }; } -interface LifecycleModule { - name: string; - rollover_alias?: string; - parse_origination_date?: boolean; - origination_date?: number; -} - export interface IndexSettings { - index?: Partial; + index?: IndicesIndexSettingsKeys; analysis?: AnalysisModule; [key: string]: any; } diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx index 24544db04498b..9cb5c481b6b50 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx @@ -22,6 +22,7 @@ import { EuiCodeBlock, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { getIndexModeLabel } from '../../../lib/index_mode_labels'; import { allowAutoCreateRadioIds } from '../../../../../common/constants'; import { serializers } from '../../../../shared_imports'; @@ -268,6 +269,19 @@ export const StepReview: React.FunctionComponent = React.memo( {getDescriptionText(serializedSettings)} + {/* Index mode */} + + + + + {getIndexModeLabel( + serializedSettings?.['index.mode'] ?? serializedSettings?.index?.mode + )} + + {/* Mappings */} { + switch (mode) { + case 'standard': + case null: + case undefined: + return i18n.translate('xpack.idxMgmt.indexModeLabels.standardModeLabel', { + defaultMessage: 'Standard', + }); + case 'logsdb': + return i18n.translate('xpack.idxMgmt.indexModeLabels.logsdbModeLabel', { + defaultMessage: 'LogsDB', + }); + case 'time_series': + return i18n.translate('xpack.idxMgmt.indexModeLabels.tsdbModeLabel', { + defaultMessage: 'Time series', + }); + default: + return mode; + } +}; diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx index 974ba6f082042..5b3bf0920c3b7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx @@ -34,6 +34,7 @@ import { EuiSpacer, } from '@elastic/eui'; +import { getIndexModeLabel } from '../../../../lib/index_mode_labels'; import { DiscoverLink } from '../../../../lib/discover_link'; import { getLifecycleValue } from '../../../../lib/data_streams'; import { SectionLoading, reactRouterNavigate } from '../../../../../shared_imports'; @@ -166,6 +167,7 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ meteringStorageSize, meteringDocsCount, lifecycle, + indexMode, } = dataStream; const getManagementDetails = () => { @@ -345,6 +347,17 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ ), dataTestSubj: 'indexTemplateDetail', }, + { + name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indexModeTitle', { + defaultMessage: 'Index mode', + }), + toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indexModeToolTip', { + defaultMessage: + "The index mode applied to the data stream's backing indices, as defined in its associated index template.", + }), + content: getIndexModeLabel(indexMode), + dataTestSubj: 'indexModeDetail', + }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionTitle', { defaultMessage: 'Effective data retention', diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx index 927907757fe7b..e91fd644f795c 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx @@ -36,6 +36,7 @@ import { humanizeTimeStamp } from '../humanize_time_stamp'; import { DataStreamsBadges } from '../data_stream_badges'; import { ConditionalWrap } from '../data_stream_detail_panel'; import { isDataStreamFullyManagedByILM } from '../../../../lib/data_streams'; +import { getIndexModeLabel } from '../../../../lib/index_mode_labels'; import { FilterListButton, Filters } from '../../components'; import { type DataStreamFilterName } from '../data_stream_list'; @@ -184,6 +185,16 @@ export const DataStreamTable: React.FunctionComponent = ({ ), }); + columns.push({ + field: 'indexMode', + name: i18n.translate('xpack.idxMgmt.dataStreamList.table.indexModeColumnTitle', { + defaultMessage: 'Index mode', + }), + truncateText: true, + sortable: true, + render: (indexMode: DataStream['indexMode']) => getIndexModeLabel(indexMode), + }); + columns.push({ field: 'lifecycle', name: ( diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx index b9dd98e21a426..a0988aec797f6 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table_pagination.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiTablePagination } from '@elastic/eui'; import { useEuiTablePersist } from '@kbn/shared-ux-table-persist'; -import { IndexModule } from '../../../../../../common'; +import { Index } from '../../../../../../common'; interface IndexTablePaginationProps { pager: any; @@ -27,7 +27,7 @@ export const IndexTablePagination = ({ readURLParams, setURLParam, }: IndexTablePaginationProps) => { - const { pageSize, onTableChange } = useEuiTablePersist({ + const { pageSize, onTableChange } = useEuiTablePersist({ tableId: 'indices', initialPageSize: pager.itemsPerPage, pageSizeOptions: PAGE_SIZE_OPTIONS, diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx index 513377714ffe0..ff06a08014f61 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx @@ -28,6 +28,7 @@ import { TemplateDeserialized } from '../../../../../../../common'; import { ILM_PAGES_POLICY_EDIT } from '../../../../../constants'; import { useIlmLocator } from '../../../../../services/use_ilm_locator'; import { allowAutoCreateRadioIds } from '../../../../../../../common/constants'; +import { getIndexModeLabel } from '../../../../../lib/index_mode_labels'; interface Props { templateDetails: TemplateDeserialized; @@ -57,6 +58,7 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails }) _meta, _kbnMeta: { isLegacy, hasDatastream }, allowAutoCreate, + template, } = templateDetails; const numIndexPatterns = indexPatterns.length; @@ -221,6 +223,17 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails }) )} + {/* Index mode */} + + + + + {getIndexModeLabel(template?.settings?.index?.mode)} + + {/* Allow auto create */} {isLegacy !== true && allowAutoCreate !== allowAutoCreateRadioIds.NO_OVERWRITE_RADIO_OPTION && ( diff --git a/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts b/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts index 2e493ca02aa79..31c0baa6c6b8c 100644 --- a/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts +++ b/x-pack/plugins/index_management/server/lib/data_stream_serialization.ts @@ -6,6 +6,7 @@ */ import { ByteSizeValue } from '@kbn/config-schema'; +import { IndexMode } from '../../common/types/data_streams'; import type { DataStream, EnhancedDataStreamFromEs, Health } from '../../common'; export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs): DataStream { @@ -28,6 +29,7 @@ export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs lifecycle, global_max_retention: globalMaxRetention, next_generation_managed_by: nextGenerationManagedBy, + index_mode: indexMode, } = dataStreamFromEs; const meteringStorageSize = meteringStorageSizeBytes !== undefined @@ -73,6 +75,7 @@ export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs globalMaxRetention, }, nextGenerationManagedBy, + indexMode: (indexMode ?? 'standard') as IndexMode, }; } diff --git a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts index 8b62c2b3a25cb..cd47b8cc9e0bb 100644 --- a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts @@ -11,6 +11,7 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { IndicesDataStream, IndicesDataStreamsStatsDataStreamsStatsItem, + IndicesGetIndexTemplateIndexTemplateItem, SecurityHasPrivilegesResponse, } from '@elastic/elasticsearch/lib/api/types'; import type { MeteringStats } from '../../../lib/types'; @@ -31,12 +32,14 @@ const enhanceDataStreams = ({ meteringStats, dataStreamsPrivileges, globalMaxRetention, + indexTemplates, }: { dataStreams: IndicesDataStream[]; dataStreamsStats?: IndicesDataStreamsStatsDataStreamsStatsItem[]; meteringStats?: MeteringStats[]; dataStreamsPrivileges?: SecurityHasPrivilegesResponse; globalMaxRetention?: string; + indexTemplates?: IndicesGetIndexTemplateIndexTemplateItem[]; }): EnhancedDataStreamFromEs[] => { return dataStreams.map((dataStream) => { const enhancedDataStream: EnhancedDataStreamFromEs = { @@ -71,6 +74,16 @@ const enhanceDataStreams = ({ } } + if (indexTemplates) { + const indexTemplate = indexTemplates.find( + (template) => template.name === dataStream.template + ); + if (indexTemplate) { + enhancedDataStream.index_mode = + indexTemplate.index_template?.template?.settings?.index?.mode; + } + } + return enhancedDataStream; }); }; @@ -152,11 +165,15 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }: ); } + const { index_templates: indexTemplates } = + await client.asCurrentUser.indices.getIndexTemplate(); + const enhancedDataStreams = enhanceDataStreams({ dataStreams, dataStreamsStats, meteringStats, dataStreamsPrivileges, + indexTemplates, }); return response.ok({ body: deserializeDataStreamList(enhancedDataStreams) }); @@ -199,17 +216,30 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }: if (dataStreams[0]) { let dataStreamsPrivileges; + let indexTemplates; if (config.isSecurityEnabled()) { dataStreamsPrivileges = await getDataStreamsPrivileges(client, [dataStreams[0].name]); } + if (dataStreams[0].template) { + const { index_templates: templates } = + await client.asCurrentUser.indices.getIndexTemplate({ + name: dataStreams[0].template, + }); + + if (templates) { + indexTemplates = templates; + } + } + const enhancedDataStreams = enhanceDataStreams({ dataStreams, dataStreamsStats, meteringStats, dataStreamsPrivileges, globalMaxRetention, + indexTemplates, }); const body = deserializeDataStream(enhancedDataStreams[0]); return response.ok({ body }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts index f3b3136200bd7..00d301e9eb706 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/hooks/use_get_data_stream_statuses.ts @@ -94,6 +94,7 @@ function toMissingDataStream({ privileges: { delete_index: true, manage_data_stream_lifecycle: true }, hidden: false, nextGenerationManagedBy: 'Data stream lifecycle', + indexMode: 'standard', }; } diff --git a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts index 791e23149aff1..2976d4eac03b4 100644 --- a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts +++ b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts @@ -73,6 +73,7 @@ export default function ({ getService }: FtrProviderContext) { health: 'yellow', indexTemplateName: testDataStreamName, hidden: false, + indexMode: 'standard', }); }); @@ -120,6 +121,7 @@ export default function ({ getService }: FtrProviderContext) { lifecycle: { enabled: true, }, + indexMode: 'standard', }); }); @@ -158,8 +160,25 @@ export default function ({ getService }: FtrProviderContext) { lifecycle: { enabled: true, }, + indexMode: 'standard', }); }); + + it('correctly returns index mode property', async () => { + const logsdbDataStreamName = 'logsdb-test-data-stream'; + const indexMode = 'logsdb'; + + await createDataStream(logsdbDataStreamName, indexMode); + + const { body: dataStream } = await supertest + .get(`${API_BASE_PATH}/data_streams/${logsdbDataStreamName}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(dataStream.indexMode).to.eql(indexMode); + + await deleteDataStream(logsdbDataStreamName); + }); }); describe('Update', () => { diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.ts b/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.ts index 65e2d733dd696..944c679c3205f 100644 --- a/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.ts +++ b/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../ftr_provider_context'; export function datastreamsHelpers(getService: FtrProviderContext['getService']) { const es = getService('es'); - const createDataStream = async (name: string) => { + const createDataStream = async (name: string, indexMode?: string) => { // A data stream requires an index template before it can be created. await es.indices.putIndexTemplate({ name, @@ -26,6 +26,11 @@ export function datastreamsHelpers(getService: FtrProviderContext['getService']) }, }, }, + settings: { + index: { + mode: indexMode, + }, + }, lifecycle: { // @ts-expect-error @elastic/elasticsearch enabled prop is not typed yet enabled: true, diff --git a/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts b/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts index d680778adc523..97ceeefbee9bd 100644 --- a/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts +++ b/x-pack/test/functional/apps/index_management/data_streams_tab/data_streams_tab.ts @@ -88,6 +88,47 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('closeDetailsButton'); }); + describe('shows the correct index mode in the details flyout', function () { + it('standard index mode', async () => { + // Open details flyout of existing data stream - it has standard index mode + await pageObjects.indexManagement.clickDataStreamNameLink(TEST_DS_NAME); + // Check that index mode detail exists and its label is "Standard" + expect(await testSubjects.exists('indexModeDetail')).to.be(true); + expect(await testSubjects.getVisibleText('indexModeDetail')).to.be('Standard'); + // Close flyout + await testSubjects.click('closeDetailsButton'); + }); + + it('logsdb index mode', async () => { + // Create an index template with a logsdb index mode + await es.indices.putIndexTemplate({ + name: `logsdb_index_template`, + index_patterns: ['test-logsdb'], + data_stream: {}, + template: { + settings: { mode: 'logsdb' }, + }, + }); + // Create a data stream matching the index pattern of the index template above + await es.indices.createDataStream({ + name: 'test-logsdb', + }); + await browser.refresh(); + // Open details flyout of data stream + await pageObjects.indexManagement.clickDataStreamNameLink('test-logsdb'); + // Check that index mode detail exists and its label is "LogsDB" + expect(await testSubjects.exists('indexModeDetail')).to.be(true); + expect(await testSubjects.getVisibleText('indexModeDetail')).to.be('LogsDB'); + // Close flyout + await testSubjects.click('closeDetailsButton'); + // Delete data stream and index template + await es.indices.deleteDataStream({ name: 'test-logsdb' }); + await es.indices.deleteIndexTemplate({ + name: `logsdb_index_template`, + }); + }); + }); + it('allows to update data retention', async () => { // Open details flyout await pageObjects.indexManagement.clickDataStreamNameLink(TEST_DS_NAME); diff --git a/x-pack/test/functional/apps/index_management/index_template_wizard.ts b/x-pack/test/functional/apps/index_management/index_template_wizard.ts index 5b49286f6182b..cf6f1bf6a44a1 100644 --- a/x-pack/test/functional/apps/index_management/index_template_wizard.ts +++ b/x-pack/test/functional/apps/index_management/index_template_wizard.ts @@ -98,6 +98,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const summaryTabContent = await testSubjects.exists('summaryTabContent'); expect(summaryTabContent).to.be(true); + // Verify that index mode is set to "Standard" + expect(await testSubjects.exists('indexModeTitle')).to.be(true); + expect(await testSubjects.getVisibleText('indexModeValue')).to.be('Standard'); + // Click Create template await pageObjects.indexManagement.clickNextButton(); }); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/datastream.ts b/x-pack/test/reporting_api_integration/reporting_and_security/datastream.ts index f116110db78f1..0f2bbeb314dfa 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/datastream.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/datastream.ts @@ -61,6 +61,7 @@ export default function ({ getService }: FtrProviderContext) { nextGenerationManagedBy: 'Index Lifecycle Management', storageSize: expect.any(String), storageSizeBytes: expect.any(Number), + indexMode: 'standard', }); }); }); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts index de3a92587d6b9..12151f1b169db 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts @@ -80,6 +80,7 @@ export default function ({ getService }: FtrProviderContext) { health: 'green', indexTemplateName: testDataStreamName, hidden: false, + indexMode: 'standard', }); }); @@ -121,6 +122,7 @@ export default function ({ getService }: FtrProviderContext) { meteringDocsCount: 0, meteringStorageSize: '0b', meteringStorageSizeBytes: 0, + indexMode: 'standard', }); }); });