[Index Management] Disable index stats on serverless (#163849)

This commit is contained in:
Alison Goryachev 2023-08-23 08:18:48 -04:00 committed by GitHub
parent 615c450b37
commit a14f76d96c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 406 additions and 102 deletions

View file

@ -37,6 +37,8 @@ xpack.license_management.enabled: false
xpack.index_management.enableIndexActions: false
# Disable legacy index templates from Index Management UI
xpack.index_management.enableLegacyTemplates: false
# Disable index stats information from Index Management UI
xpack.index_management.enableIndexStats: false
# Keep deeplinks visible so that they are shown in the sidenav
dev_tools.deeplinks.navLinkStatus: visible

View file

@ -243,6 +243,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.index_management.enableIndexActions (any)',
'xpack.index_management.enableLegacyTemplates (any)',
'xpack.index_management.dev.enableIndexDetailsPage (boolean)',
'xpack.index_management.enableIndexStats (any)',
'xpack.infra.sources.default.fields.message (array)',
/**
* xpack.infra.logs is conditional and will resolve to an object of properties

View file

@ -80,7 +80,7 @@ const indexWithLifecyclePolicy: Index = {
},
};
const indexWithLifecycleError = {
const indexWithLifecycleError: Index = {
health: 'yellow',
status: 'open',
name: 'testy3',

View file

@ -60,6 +60,7 @@ const appDependencies = {
config: {
enableLegacyTemplates: true,
enableIndexActions: true,
enableIndexStats: true,
},
} as any;

View file

@ -19,6 +19,7 @@ export type TestSubjects =
| 'deleteSystemTemplateCallOut'
| 'deleteTemplateButton'
| 'deleteTemplatesConfirmation'
| 'descriptionTitle'
| 'documentationLink'
| 'emptyPrompt'
| 'forcemergeIndexMenuButton'

View file

@ -62,14 +62,13 @@ describe('<IndexManagementHome />', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadIndicesResponse([]);
testBed = await setup(httpSetup);
await act(async () => {
const { component } = testBed;
await nextTick();
component.update();
testBed = await setup(httpSetup);
});
const { component } = testBed;
component.update();
});
test('toggles the include hidden button through URL hash correctly', () => {
@ -423,4 +422,103 @@ describe('<IndexManagementHome />', () => {
expect(exists('updateIndexSettingsErrorCallout')).toBe(true);
});
});
describe('Index stats', () => {
const indexName = 'test';
beforeEach(async () => {
httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]);
await act(async () => {
testBed = await setup(httpSetup);
});
const { component } = testBed;
component.update();
});
test('renders the table column with index stats by default', () => {
const { table } = testBed;
const { tableCellsValues } = table.getMetaData('indexTable');
expect(tableCellsValues).toEqual([
['', 'test', 'green', 'open', '1', '1', '10000', '156kb', ''],
]);
});
test('renders index stats in details flyout by default', async () => {
const { component, find } = testBed;
await act(async () => {
find('indexTableIndexNameLink').at(0).simulate('click');
});
component.update();
const descriptions = find('descriptionTitle');
const descriptionText = descriptions
.map((description) => {
return description.text();
})
.sort();
expect(descriptionText).toEqual([
'Aliases',
'Docs count',
'Docs deleted',
'Health',
'Primaries',
'Primary storage size',
'Replicas',
'Status',
'Storage size',
]);
});
describe('Disabled', () => {
beforeEach(async () => {
await act(async () => {
testBed = await setup(httpSetup, {
config: {
enableLegacyTemplates: true,
enableIndexActions: true,
enableIndexStats: false,
},
});
});
const { component } = testBed;
component.update();
});
test('hides index stats information from table', async () => {
const { table } = testBed;
const { tableCellsValues } = table.getMetaData('indexTable');
expect(tableCellsValues).toEqual([['', 'test', '1', '1', '']]);
});
test('hides index stats information from details panel', async () => {
const { component, find } = testBed;
await act(async () => {
find('indexTableIndexNameLink').at(0).simulate('click');
});
component.update();
const descriptions = find('descriptionTitle');
const descriptionText = descriptions
.map((description) => {
return description.text();
})
.sort();
expect(descriptionText).toEqual(['Aliases', 'Primaries', 'Replicas']);
});
});
});
});

View file

@ -172,6 +172,7 @@ describe('index table', () => {
config: {
enableLegacyTemplates: true,
enableIndexActions: true,
enableIndexStats: true,
},
};

View file

@ -5,6 +5,12 @@
* 2.0.
*/
import {
HealthStatus,
IndicesStatsIndexMetadataState,
Uuid,
} from '@elastic/elasticsearch/lib/api/types';
interface IndexModule {
number_of_shards: number | string;
codec: string;
@ -50,21 +56,21 @@ export interface IndexSettings {
analysis?: AnalysisModule;
[key: string]: any;
}
export interface Index {
health?: string;
status?: string;
name: string;
uuid?: string;
primary?: number | string;
replica?: number | string;
documents: number;
documents_deleted: number;
size: string;
primary_size: string;
isFrozen: boolean;
hidden: boolean;
aliases: string | string[];
data_stream?: string;
[key: string]: any;
// The types from here below represent information returned from the index stats API;
// treated optional as the stats API is not available on serverless
health?: HealthStatus;
status?: IndicesStatsIndexMetadataState;
uuid?: Uuid;
documents?: number;
size?: string;
primary_size?: string;
documents_deleted?: number;
}

View file

@ -48,6 +48,7 @@ export interface AppDependencies {
enableIndexActions: boolean;
enableLegacyTemplates: boolean;
enableIndexDetailsPage: boolean;
enableIndexStats: boolean;
};
history: ScopedHistory;
setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs'];

View file

@ -56,6 +56,7 @@ export async function mountManagementSection({
enableIndexActions = true,
enableLegacyTemplates = true,
enableIndexDetailsPage = false,
enableIndexStats = true,
}: {
coreSetup: CoreSetup<StartDependencies>;
usageCollection: UsageCollectionSetup;
@ -66,6 +67,7 @@ export async function mountManagementSection({
enableIndexActions?: boolean;
enableLegacyTemplates?: boolean;
enableIndexDetailsPage?: boolean;
enableIndexStats?: boolean;
}) {
const { element, setBreadcrumbs, history, theme$ } = params;
const [core, startDependencies] = await coreSetup.getStartServices();
@ -111,6 +113,7 @@ export async function mountManagementSection({
enableIndexActions,
enableLegacyTemplates,
enableIndexDetailsPage,
enableIndexStats,
},
history,
setBreadcrumbs,

View file

@ -34,7 +34,7 @@ import { IndexActionsContextMenu } from '../index_actions_context_menu';
import { ShowJson } from './show_json';
import { Summary } from './summary';
import { EditSettingsJson } from './edit_settings_json';
import { useServices } from '../../../../app_context';
import { useServices, useAppContext } from '../../../../app_context';
import { renderDiscoverLink } from '../../../../lib/render_discover_link';
const tabToHumanizedMap = {
@ -58,12 +58,19 @@ const tabToHumanizedMap = {
),
};
const tabs = [TAB_SUMMARY, TAB_SETTINGS, TAB_MAPPING, TAB_STATS, TAB_EDIT_SETTINGS];
const getTabs = (showStats) => {
if (showStats) {
return [TAB_SUMMARY, TAB_SETTINGS, TAB_MAPPING, TAB_STATS, TAB_EDIT_SETTINGS];
}
return [TAB_SUMMARY, TAB_SETTINGS, TAB_MAPPING, TAB_EDIT_SETTINGS];
};
export const DetailPanel = ({ panelType, indexName, index, openDetailPanel, closeDetailPanel }) => {
const { extensionsService } = useServices();
const { config } = useAppContext();
const renderTabs = () => {
const tabs = getTabs(config.enableIndexStats);
return tabs.map((tab, i) => {
const isSelected = tab === panelType;
return (

View file

@ -21,36 +21,43 @@ import {
import { DataHealth } from '../../../../../components';
import { AppContextConsumer } from '../../../../../app_context';
const getHeaders = () => {
return {
health: i18n.translate('xpack.idxMgmt.summary.headers.healthHeader', {
defaultMessage: 'Health',
}),
status: i18n.translate('xpack.idxMgmt.summary.headers.statusHeader', {
defaultMessage: 'Status',
}),
const getHeaders = (showStats) => {
const baseHeaders = {
primary: i18n.translate('xpack.idxMgmt.summary.headers.primaryHeader', {
defaultMessage: 'Primaries',
}),
replica: i18n.translate('xpack.idxMgmt.summary.headers.replicaHeader', {
defaultMessage: 'Replicas',
}),
documents: i18n.translate('xpack.idxMgmt.summary.headers.documentsHeader', {
defaultMessage: 'Docs count',
}),
documents_deleted: i18n.translate('xpack.idxMgmt.summary.headers.deletedDocumentsHeader', {
defaultMessage: 'Docs deleted',
}),
size: i18n.translate('xpack.idxMgmt.summary.headers.storageSizeHeader', {
defaultMessage: 'Storage size',
}),
primary_size: i18n.translate('xpack.idxMgmt.summary.headers.primaryStorageSizeHeader', {
defaultMessage: 'Primary storage size',
}),
aliases: i18n.translate('xpack.idxMgmt.summary.headers.aliases', {
defaultMessage: 'Aliases',
}),
};
if (showStats) {
return {
...baseHeaders,
health: i18n.translate('xpack.idxMgmt.summary.headers.healthHeader', {
defaultMessage: 'Health',
}),
status: i18n.translate('xpack.idxMgmt.summary.headers.statusHeader', {
defaultMessage: 'Status',
}),
documents: i18n.translate('xpack.idxMgmt.summary.headers.documentsHeader', {
defaultMessage: 'Docs count',
}),
documents_deleted: i18n.translate('xpack.idxMgmt.summary.headers.deletedDocumentsHeader', {
defaultMessage: 'Docs deleted',
}),
size: i18n.translate('xpack.idxMgmt.summary.headers.storageSizeHeader', {
defaultMessage: 'Storage size',
}),
primary_size: i18n.translate('xpack.idxMgmt.summary.headers.primaryStorageSizeHeader', {
defaultMessage: 'Primary storage size',
}),
};
}
return baseHeaders;
};
export class Summary extends React.PureComponent {
@ -67,9 +74,9 @@ export class Summary extends React.PureComponent {
});
}
buildRows() {
buildRows(config) {
const { index } = this.props;
const headers = getHeaders();
const headers = getHeaders(config.enableIndexStats);
const rows = {
left: [],
right: [],
@ -84,7 +91,7 @@ export class Summary extends React.PureComponent {
content = content.join(', ');
}
const cell = [
<EuiDescriptionListTitle key={fieldName}>
<EuiDescriptionListTitle key={fieldName} data-test-subj="descriptionTitle">
<strong>{headers[fieldName]}</strong>
</EuiDescriptionListTitle>,
<EuiDescriptionListDescription key={fieldName + '_desc'}>
@ -103,8 +110,8 @@ export class Summary extends React.PureComponent {
render() {
return (
<AppContextConsumer>
{({ services, core }) => {
const { left, right } = this.buildRows();
{({ services, core, config }) => {
const { left, right } = this.buildRows(config);
const additionalContent = this.getAdditionalContent(
services.extensionsService,
core.getUrlForApp

View file

@ -51,31 +51,46 @@ import { renderBadges } from '../../../../lib/render_badges';
import { NoMatch, DataHealth } from '../../../../components';
import { IndexActionsContextMenu } from '../index_actions_context_menu';
const HEADERS = {
name: i18n.translate('xpack.idxMgmt.indexTable.headers.nameHeader', {
const getHeaders = ({ showIndexStats }) => {
const headers = {};
headers.name = i18n.translate('xpack.idxMgmt.indexTable.headers.nameHeader', {
defaultMessage: 'Name',
}),
health: i18n.translate('xpack.idxMgmt.indexTable.headers.healthHeader', {
defaultMessage: 'Health',
}),
status: i18n.translate('xpack.idxMgmt.indexTable.headers.statusHeader', {
defaultMessage: 'Status',
}),
primary: i18n.translate('xpack.idxMgmt.indexTable.headers.primaryHeader', {
});
if (showIndexStats) {
headers.health = i18n.translate('xpack.idxMgmt.indexTable.headers.healthHeader', {
defaultMessage: 'Health',
});
headers.status = i18n.translate('xpack.idxMgmt.indexTable.headers.statusHeader', {
defaultMessage: 'Status',
});
}
headers.primary = i18n.translate('xpack.idxMgmt.indexTable.headers.primaryHeader', {
defaultMessage: 'Primaries',
}),
replica: i18n.translate('xpack.idxMgmt.indexTable.headers.replicaHeader', {
});
headers.replica = i18n.translate('xpack.idxMgmt.indexTable.headers.replicaHeader', {
defaultMessage: 'Replicas',
}),
documents: i18n.translate('xpack.idxMgmt.indexTable.headers.documentsHeader', {
defaultMessage: 'Docs count',
}),
size: i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', {
defaultMessage: 'Storage size',
}),
data_stream: i18n.translate('xpack.idxMgmt.indexTable.headers.dataStreamHeader', {
});
if (showIndexStats) {
headers.documents = i18n.translate('xpack.idxMgmt.indexTable.headers.documentsHeader', {
defaultMessage: 'Docs count',
});
headers.size = i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', {
defaultMessage: 'Storage size',
});
}
headers.data_stream = i18n.translate('xpack.idxMgmt.indexTable.headers.dataStreamHeader', {
defaultMessage: 'Data stream',
}),
});
return headers;
};
export class IndexTable extends Component {
@ -246,9 +261,10 @@ export class IndexTable extends Component {
return indexOfUnselectedItem === -1;
};
buildHeader() {
buildHeader(config) {
const { sortField, isSortAscending } = this.props;
return Object.entries(HEADERS).map(([fieldName, label]) => {
const headers = getHeaders({ showIndexStats: config.enableIndexStats });
return Object.entries(headers).map(([fieldName, label]) => {
const isSorted = sortField === fieldName;
return (
<EuiTableHeaderCell
@ -302,8 +318,9 @@ export class IndexTable extends Component {
return value;
}
buildRowCells(index, appServices) {
return Object.keys(HEADERS).map((fieldName) => {
buildRowCells(index, appServices, config) {
const headers = getHeaders({ showIndexStats: config.enableIndexStats });
return Object.keys(headers).map((fieldName) => {
const { name } = index;
const value = index[fieldName];
@ -363,7 +380,7 @@ export class IndexTable extends Component {
});
}
buildRows(appServices) {
buildRows(appServices, config) {
const { indices = [], detailPanelIndexName } = this.props;
return indices.map((index) => {
const { name } = index;
@ -388,7 +405,7 @@ export class IndexTable extends Component {
})}
/>
</EuiTableRowCellCheckbox>
{this.buildRowCells(index, appServices)}
{this.buildRowCells(index, appServices, config)}
</EuiTableRow>
);
});
@ -479,7 +496,7 @@ export class IndexTable extends Component {
return (
<AppContextConsumer>
{({ services }) => {
{({ services, config }) => {
const { extensionsService } = services;
return (
@ -639,10 +656,10 @@ export class IndexTable extends Component {
)}
/>
</EuiTableHeaderCellCheckbox>
{this.buildHeader()}
{this.buildHeader(config)}
</EuiTableHeader>
<EuiTableBody>{this.buildRows(services)}</EuiTableBody>
<EuiTableBody>{this.buildRows(services, config)}</EuiTableBody>
</EuiTable>
</div>
) : (

View file

@ -123,15 +123,19 @@ export const getPageOfIndices = createSelector(
const { firstItemIndex, lastItemIndex } = pager;
const pagedIndexes = sortedIndexes.slice(firstItemIndex, lastItemIndex + 1);
return pagedIndexes.map((index) => {
const status =
indexStatusLabels[rowStatuses[index.name]] || // user friendly version of row status
rowStatuses[index.name] || // row status
indexStatusLabels[index.status] || // user friendly version of index status
index.status; // index status
return {
...index,
status,
};
if (index.status) {
const status =
indexStatusLabels[rowStatuses[index.name]] || // user friendly version of row status
rowStatuses[index.name] || // row status
indexStatusLabels[index.status] || // user friendly version of index status
index.status; // index status
return {
...index,
status,
};
}
return index;
});
}
);

View file

@ -40,6 +40,7 @@ export class IndexMgmtUIPlugin {
ui: { enabled: isIndexManagementUiEnabled },
enableIndexActions,
enableLegacyTemplates,
enableIndexStats,
dev: { enableIndexDetailsPage },
} = this.ctx.config.get<ClientConfigType>();
@ -62,6 +63,7 @@ export class IndexMgmtUIPlugin {
enableIndexActions,
enableLegacyTemplates,
enableIndexDetailsPage,
enableIndexStats,
});
},
});

View file

@ -31,6 +31,7 @@ export interface ClientConfigType {
};
enableIndexActions?: boolean;
enableLegacyTemplates?: boolean;
enableIndexStats?: boolean;
dev: {
enableIndexDetailsPage?: boolean;
};

View file

@ -33,6 +33,11 @@ const schemaLatest = schema.object(
serverless: schema.boolean({ defaultValue: true }),
}),
dev: schema.object({ enableIndexDetailsPage: schema.boolean({ defaultValue: false }) }),
enableIndexStats: offeringBasedSchema({
// Index stats information is disabled in serverless; refer to the serverless.yml file as the source of truth
// We take this approach in order to have a central place (serverless.yml) for serverless config across Kibana
serverless: schema.boolean({ defaultValue: true }),
}),
},
{ defaultValue: undefined }
);
@ -45,6 +50,7 @@ const configLatest: PluginConfigDescriptor<IndexManagementConfig> = {
dev: {
enableIndexDetailsPage: true,
},
enableIndexStats: true,
},
schema: schemaLatest,
deprecations: () => [],

View file

@ -9,9 +9,11 @@ import { ByteSizeValue } from '@kbn/config-schema';
import { IScopedClusterClient } from '@kbn/core/server';
import { IndexDataEnricher } from '../services';
import { Index } from '..';
import { RouteDependencies } from '../types';
async function fetchIndicesCall(
client: IScopedClusterClient,
config: RouteDependencies['config'],
indexNames?: string[]
): Promise<Index[]> {
const indexNamesString = indexNames && indexNames.length ? indexNames.join(',') : '*';
@ -38,41 +40,77 @@ async function fetchIndicesCall(
return [];
}
const { indices: indicesStats = {} } = await client.asCurrentUser.indices.stats({
const indicesNames = Object.keys(indices);
// Return response without index stats, if isIndexStatsEnabled === false
if (config.isIndexStatsEnabled === false) {
return indicesNames.map((indexName: string) => {
const indexData = indices[indexName];
const aliases = Object.keys(indexData.aliases!);
return {
name: indexName,
primary: indexData.settings?.index?.number_of_shards,
replica: indexData.settings?.index?.number_of_replicas,
isFrozen: indexData.settings?.index?.frozen === 'true',
aliases: aliases.length ? aliases : 'none',
hidden: indexData.settings?.index?.hidden === 'true',
data_stream: indexData.data_stream,
};
});
}
const { indices: indicesStats } = await client.asCurrentUser.indices.stats({
index: indexNamesString,
expand_wildcards: ['hidden', 'all'],
forbid_closed_indices: false,
metric: ['docs', 'store'],
});
const indicesNames = Object.keys(indices);
return indicesNames.map((indexName: string) => {
const indexData = indices[indexName];
const indexStats = indicesStats[indexName];
const aliases = Object.keys(indexData.aliases!);
return {
health: indexStats?.health,
status: indexStats?.status,
const baseResponse = {
name: indexName,
uuid: indexStats?.uuid,
primary: indexData.settings?.index?.number_of_shards,
replica: indexData.settings?.index?.number_of_replicas,
documents: indexStats?.primaries?.docs?.count ?? 0,
documents_deleted: indexStats?.primaries?.docs?.deleted ?? 0,
size: new ByteSizeValue(indexStats?.total?.store?.size_in_bytes ?? 0).toString(),
primary_size: new ByteSizeValue(indexStats?.primaries?.store?.size_in_bytes ?? 0).toString(),
isFrozen: indexData.settings?.index?.frozen === 'true',
aliases: aliases.length ? aliases : 'none',
hidden: indexData.settings?.index?.hidden === 'true',
data_stream: indexData.data_stream,
};
if (indicesStats) {
const indexStats = indicesStats[indexName];
return {
...baseResponse,
health: indexStats?.health,
status: indexStats?.status,
uuid: indexStats?.uuid,
documents: indexStats?.primaries?.docs?.count ?? 0,
documents_deleted: indexStats?.primaries?.docs?.deleted ?? 0,
size: new ByteSizeValue(indexStats?.total?.store?.size_in_bytes ?? 0).toString(),
primary_size: new ByteSizeValue(
indexStats?.primaries?.store?.size_in_bytes ?? 0
).toString(),
};
}
return baseResponse;
});
}
export const fetchIndices = async (
client: IScopedClusterClient,
indexDataEnricher: IndexDataEnricher,
indexNames?: string[]
) => {
const indices = await fetchIndicesCall(client, indexNames);
export const fetchIndices = async ({
client,
indexDataEnricher,
config,
indexNames,
}: {
client: IScopedClusterClient;
indexDataEnricher: IndexDataEnricher;
config: RouteDependencies['config'];
indexNames?: string[];
}) => {
const indices = await fetchIndicesCall(client, config, indexNames);
return await indexDataEnricher.enrichIndices(indices, client);
};

View file

@ -55,6 +55,7 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
config: {
isSecurityEnabled: () => security !== undefined && security.license.isEnabled(),
isLegacyTemplatesEnabled: this.config.enableLegacyTemplates,
isIndexStatsEnabled: this.config.enableIndexStats,
},
indexDataEnricher: this.indexDataEnricher,
lib: {

View file

@ -47,6 +47,7 @@ describe('GET privileges', () => {
config: {
isSecurityEnabled: () => true,
isLegacyTemplatesEnabled: true,
isIndexStatsEnabled: true,
},
indexDataEnricher: mockedIndexDataEnricher,
lib: {
@ -114,6 +115,7 @@ describe('GET privileges', () => {
config: {
isSecurityEnabled: () => false,
isLegacyTemplatesEnabled: true,
isIndexStatsEnabled: true,
},
indexDataEnricher: mockedIndexDataEnricher,
lib: {

View file

@ -13,13 +13,14 @@ export function registerListRoute({
router,
indexDataEnricher,
lib: { handleEsError },
config,
}: RouteDependencies) {
router.get(
{ path: addBasePath('/indices'), validate: false },
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
try {
const indices = await fetchIndices(client, indexDataEnricher);
const indices = await fetchIndices({ client, indexDataEnricher, config });
return response.ok({ body: indices });
} catch (error) {
return handleEsError({ error, response });

View file

@ -21,6 +21,7 @@ export function registerReloadRoute({
router,
indexDataEnricher,
lib: { handleEsError },
config,
}: RouteDependencies) {
router.post(
{ path: addBasePath('/indices/reload'), validate: { body: bodySchema } },
@ -29,7 +30,7 @@ export function registerReloadRoute({
const { indexNames = [] } = (request.body as typeof bodySchema.type) ?? {};
try {
const indices = await fetchIndices(client, indexDataEnricher, indexNames);
const indices = await fetchIndices({ client, indexDataEnricher, config, indexNames });
return response.ok({ body: indices });
} catch (error) {
return handleEsError({ error, response });

View file

@ -23,11 +23,14 @@ export class ApiRoutes {
registerIndicesRoutes(dependencies);
registerTemplateRoutes(dependencies);
registerSettingsRoutes(dependencies);
registerStatsRoute(dependencies);
registerMappingRoute(dependencies);
registerComponentTemplateRoutes(dependencies);
registerNodesRoute(dependencies);
registerEnrichPoliciesRoute(dependencies);
if (dependencies.config.isIndexStatsEnabled !== false) {
registerStatsRoute(dependencies);
}
}
start() {}

View file

@ -13,6 +13,7 @@ export const routeDependencies: Omit<RouteDependencies, 'router'> = {
config: {
isSecurityEnabled: jest.fn().mockReturnValue(true),
isLegacyTemplatesEnabled: true,
isIndexStatsEnabled: true,
},
indexDataEnricher: new IndexDataEnricher(),
lib: {

View file

@ -24,6 +24,7 @@ export interface RouteDependencies {
config: {
isSecurityEnabled: () => boolean;
isLegacyTemplatesEnabled: boolean;
isIndexStatsEnabled: boolean;
};
indexDataEnricher: IndexDataEnricher;
lib: {

View file

@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Index Management APIs', function () {
loadTestFile(require.resolve('./index_templates'));
loadTestFile(require.resolve('./indices'));
});
}

View file

@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from 'expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
const API_BASE_PATH = '/api/index_management';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const es = getService('es');
const log = getService('log');
describe('Indices', function () {
const indexName = `index-${Math.random()}`;
before(async () => {
// Create a new index to test against
try {
await es.indices.create({ index: indexName });
} catch (err) {
log.debug('[Setup error] Error creating index');
throw err;
}
});
after(async () => {
// Cleanup index created for testing purposes
try {
await es.indices.delete({
index: indexName,
});
} catch (err) {
log.debug('[Cleanup error] Error deleting index');
throw err;
}
});
describe('get all', () => {
it('should list indices with the expected parameters', async () => {
const { body: indices } = await supertest
.get(`${API_BASE_PATH}/indices`)
.set('kbn-xsrf', 'xxx')
.set('x-elastic-internal-origin', 'xxx')
.expect(200);
const indexFound = indices.find((index: { name: string }) => index.name === indexName);
expect(indexFound).toBeTruthy();
const expectedKeys = ['aliases', 'hidden', 'isFrozen', 'primary', 'replica', 'name'].sort();
expect(Object.keys(indexFound).sort()).toEqual(expectedKeys);
});
});
});
}

View file

@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default ({ loadTestFile }: FtrProviderContext) => {
describe('Index Management', function () {
loadTestFile(require.resolve('./index_templates'));
loadTestFile(require.resolve('./indices'));
});
};

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const testSubjects = getService('testSubjects');
const pageObjects = getPageObjects(['common', 'indexManagement', 'header']);
const browser = getService('browser');
const security = getService('security');
const retry = getService('retry');
describe('Indices', function () {
before(async () => {
await security.testUser.setRoles(['index_management_user']);
await pageObjects.common.navigateToApp('indexManagement');
// Navigate to the indices tab
await pageObjects.indexManagement.changeTabs('indicesTab');
});
it('renders the indices tab', async () => {
await retry.waitFor('indices list to be visible', async () => {
return await testSubjects.exists('indicesList');
});
const url = await browser.getCurrentUrl();
expect(url).to.contain(`/indices`);
});
});
};