mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Adding frozen indices support index management (#28855)
* adding frozen badge and refactoring list and reload server routes * adding freeze and unfreeze actions to index management * adding badge to detail panel and factoring rendering of badges into lib function * fixing issue with sorted name field and ellipsis * modifying button label for freeze confirmation * copy edit * correcting i18n label * fixing duplicate i18n id issue * fixing another duplicate i18n id * updating jest snapshots * adding color to badge specification * removing icons from index management context menu
This commit is contained in:
parent
8e664fa5e5
commit
57849623fa
21 changed files with 425 additions and 153 deletions
|
@ -102,6 +102,7 @@ Array [
|
|||
"Refresh indices",
|
||||
"Clear indices cache",
|
||||
"Flush indices",
|
||||
"Freeze indices",
|
||||
"Delete indices",
|
||||
]
|
||||
`;
|
||||
|
@ -127,6 +128,7 @@ Array [
|
|||
"Refresh index",
|
||||
"Clear index cache",
|
||||
"Flush index",
|
||||
"Freeze index",
|
||||
"Delete index",
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
const summaryExtensions = [];
|
||||
export const addSummaryExtension = (summaryExtension)=> {
|
||||
summaryExtensions.push(summaryExtension);
|
||||
|
@ -39,7 +39,17 @@ export const addToggleExtension = (toggleExtension)=> {
|
|||
export const getToggleExtensions = () => {
|
||||
return toggleExtensions;
|
||||
};
|
||||
const badgeExtensions = [];
|
||||
const badgeExtensions = [
|
||||
{
|
||||
matchIndex: (index) => {
|
||||
return index.isFrozen;
|
||||
},
|
||||
label: i18n.translate('xpack.idxMgmt.frozenBadgeLabel', {
|
||||
defaultMessage: 'Frozen',
|
||||
}),
|
||||
color: 'primary'
|
||||
}
|
||||
];
|
||||
export const addBadgeExtension = (badgeExtension)=> {
|
||||
badgeExtensions.push(badgeExtension);
|
||||
};
|
||||
|
|
27
x-pack/plugins/index_management/public/lib/render_badges.js
Normal file
27
x-pack/plugins/index_management/public/lib/render_badges.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
import { getBadgeExtensions } from '../index_management_extensions';
|
||||
export const renderBadges = (index) => {
|
||||
const badgeLabels = [];
|
||||
getBadgeExtensions().forEach(({ matchIndex, label, color }) => {
|
||||
if (matchIndex(index)) {
|
||||
badgeLabels.push(
|
||||
<Fragment key={label}>
|
||||
{' '}<EuiBadge color={color}>{label}</EuiBadge>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
{badgeLabels}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -10,7 +10,7 @@ import { Route } from 'react-router-dom';
|
|||
import { ShowJson } from './show_json';
|
||||
import { Summary } from './summary';
|
||||
import { EditSettingsJson } from './edit_settings_json';
|
||||
|
||||
import { renderBadges } from '../../../../lib/render_badges';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
|
@ -50,7 +50,6 @@ export class DetailPanel extends Component {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { panelType, indexName, index, closeDetailPanel } = this.props;
|
||||
if (!panelType) {
|
||||
|
@ -126,7 +125,7 @@ export class DetailPanel extends Component {
|
|||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle size="l" id="indexDetailsFlyoutTitle">
|
||||
<h2>{indexName}</h2>
|
||||
<h2>{indexName}{renderBadges(index)}</h2>
|
||||
</EuiTitle>
|
||||
{index ? <EuiTabs>{this.renderTabs()}</EuiTabs> : null }
|
||||
</EuiFlyoutHeader>
|
||||
|
|
|
@ -17,7 +17,9 @@ import {
|
|||
refreshIndices,
|
||||
openDetailPanel,
|
||||
performExtensionAction,
|
||||
reloadIndices
|
||||
reloadIndices,
|
||||
freezeIndices,
|
||||
unfreezeIndices,
|
||||
} from '../../../../store/actions';
|
||||
|
||||
import {
|
||||
|
@ -57,6 +59,12 @@ const mapDispatchToProps = (dispatch, { indexNames }) => {
|
|||
refreshIndices: () => {
|
||||
dispatch(refreshIndices({ indexNames }));
|
||||
},
|
||||
freezeIndices: () => {
|
||||
dispatch(freezeIndices({ indexNames }));
|
||||
},
|
||||
unfreezeIndices: () => {
|
||||
dispatch(unfreezeIndices({ indexNames }));
|
||||
},
|
||||
forcemergeIndices: (maxNumSegments) => {
|
||||
dispatch(forcemergeIndices({ indexNames, maxNumSegments }));
|
||||
},
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiIcon,
|
||||
EuiPopover,
|
||||
EuiSpacer,
|
||||
EuiConfirmModal,
|
||||
|
@ -57,11 +56,14 @@ class IndexActionsContextMenuUi extends Component {
|
|||
performExtensionAction,
|
||||
indices,
|
||||
intl,
|
||||
reloadIndices
|
||||
reloadIndices,
|
||||
unfreezeIndices,
|
||||
} = this.props;
|
||||
const allOpen = all(indexNames, indexName => {
|
||||
return indexStatusByName[indexName] === INDEX_OPEN;
|
||||
});
|
||||
const allFrozen = all(indices, (index) => index.isFrozen);
|
||||
const allUnfrozen = all(indices, (index) => !index.isFrozen);
|
||||
const selectedIndexCount = indexNames.length;
|
||||
const items = [];
|
||||
if (!detailPanel && selectedIndexCount === 1) {
|
||||
|
@ -70,7 +72,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.showIndexSettingsLabel',
|
||||
defaultMessage: 'Show {selectedIndexCount, plural, one {index} other {indices} } settings',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="indexSettings" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(showSettings);
|
||||
}
|
||||
|
@ -80,7 +81,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.showIndexMappingLabel',
|
||||
defaultMessage: 'Show {selectedIndexCount, plural, one {index} other {indices} } mapping',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="indexMapping" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(showMapping);
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.showIndexStatsLabel',
|
||||
defaultMessage: 'Show {selectedIndexCount, plural, one {index} other {indices} } stats',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="stats" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(showStats);
|
||||
}
|
||||
|
@ -102,7 +101,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.editIndexSettingsLabel',
|
||||
defaultMessage: 'Edit {selectedIndexCount, plural, one {index} other {indices} } settings',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="indexEdit" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(editIndex);
|
||||
}
|
||||
|
@ -114,7 +112,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.closeIndexLabel',
|
||||
defaultMessage: 'Close {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="indexClose" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(closeIndices);
|
||||
}
|
||||
|
@ -124,7 +121,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.forceMergeIndexLabel',
|
||||
defaultMessage: 'Force merge {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="merge" />,
|
||||
onClick: () => {
|
||||
this.closePopover();
|
||||
this.setState({ renderConfirmModal: this.renderForcemergeSegmentsModal });
|
||||
|
@ -135,7 +131,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.refreshIndexLabel',
|
||||
defaultMessage: 'Refresh {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="refresh" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(refreshIndices);
|
||||
}
|
||||
|
@ -145,7 +140,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.clearIndexCacheLabel',
|
||||
defaultMessage: 'Clear {selectedIndexCount, plural, one {index} other {indices} } cache',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="broom" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(clearCacheIndices);
|
||||
}
|
||||
|
@ -155,18 +149,38 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.flushIndexLabel',
|
||||
defaultMessage: 'Flush {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="indexFlush" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(flushIndices);
|
||||
}
|
||||
});
|
||||
if (allFrozen) {
|
||||
items.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.unfreezeIndexLabel',
|
||||
defaultMessage: 'Unfreeze {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(unfreezeIndices);
|
||||
}
|
||||
});
|
||||
} else if (allUnfrozen) {
|
||||
items.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.freezeIndexLabel',
|
||||
defaultMessage: 'Freeze {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
onClick: () => {
|
||||
this.closePopover();
|
||||
this.setState({ renderConfirmModal: this.renderConfirmFreezeModal });
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
items.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.openIndexLabel',
|
||||
defaultMessage: 'Open {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="indexOpen" />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(openIndices);
|
||||
}
|
||||
|
@ -177,7 +191,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
id: 'xpack.idxMgmt.indexActionsMenu.deleteIndexLabel',
|
||||
defaultMessage: 'Delete {selectedIndexCount, plural, one {index} other {indices} }',
|
||||
}, { selectedIndexCount }),
|
||||
icon: <EuiIcon type="trash" />,
|
||||
onClick: () => {
|
||||
this.closePopover();
|
||||
this.setState({ renderConfirmModal: this.renderConfirmDeleteModal });
|
||||
|
@ -186,11 +199,10 @@ class IndexActionsContextMenuUi extends Component {
|
|||
getActionExtensions().forEach((actionExtension) => {
|
||||
const actionExtensionDefinition = actionExtension(indices, reloadIndices);
|
||||
if (actionExtensionDefinition) {
|
||||
const { buttonLabel, requestMethod, successMessage, icon, renderConfirmModal } = actionExtensionDefinition;
|
||||
const { buttonLabel, requestMethod, successMessage, renderConfirmModal } = actionExtensionDefinition;
|
||||
if (requestMethod) {
|
||||
items.push({
|
||||
name: buttonLabel,
|
||||
icon: <EuiIcon type={icon} />,
|
||||
onClick: () => {
|
||||
this.closePopoverAndExecute(async () => {
|
||||
await performExtensionAction(requestMethod, successMessage);
|
||||
|
@ -200,7 +212,6 @@ class IndexActionsContextMenuUi extends Component {
|
|||
} else {
|
||||
items.push({
|
||||
name: buttonLabel,
|
||||
icon: <EuiIcon type={icon} />,
|
||||
onClick: () => {
|
||||
this.closePopover();
|
||||
this.setState({ renderConfirmModal });
|
||||
|
@ -416,6 +427,91 @@ class IndexActionsContextMenuUi extends Component {
|
|||
</EuiOverlayMask>
|
||||
);
|
||||
};
|
||||
renderConfirmFreezeModal = () => {
|
||||
const oneIndexSelected = this.oneIndexSelected();
|
||||
const entity = this.getEntity(oneIndexSelected);
|
||||
const { freezeIndices, indexNames, intl } = this.props;
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={
|
||||
intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.freezeEntity.confirmModal.modalTitle',
|
||||
defaultMessage: 'Confirm Freeze {entity}',
|
||||
}, { entity })
|
||||
}
|
||||
onCancel={this.closeConfirmModal}
|
||||
onConfirm={() => this.closePopoverAndExecute(freezeIndices)}
|
||||
cancelButtonText={
|
||||
intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.freezeEntity.confirmModal.cancelButtonText',
|
||||
defaultMessage: 'Cancel',
|
||||
})
|
||||
}
|
||||
confirmButtonText={
|
||||
intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.freezeEntity.confirmModal.confirmButtonText',
|
||||
defaultMessage: 'Freeze {entity}',
|
||||
}, { entity })
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexActionsMenu.freezeEntity.freezeDescription"
|
||||
defaultMessage="You are about to freeze {oneIndexSelected, plural, one {this} other {these}}"
|
||||
values={{ oneIndexSelected: oneIndexSelected ? 1 : 0 }}
|
||||
/>
|
||||
{' '}
|
||||
{entity}:
|
||||
</p>
|
||||
<ul>
|
||||
{indexNames.map(indexName => (
|
||||
<li key={indexName}>{indexName}</li>
|
||||
))}
|
||||
</ul>
|
||||
<EuiCallOut
|
||||
title={
|
||||
intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.freezeEntity.proceedWithCautionCallOutTitle',
|
||||
defaultMessage: 'Proceed with caution',
|
||||
})
|
||||
}
|
||||
color="warning"
|
||||
iconType="help"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.idxMgmt.indexActionsMenu.freezeEntity.freezeEntityWarningDescription"
|
||||
defaultMessage="
|
||||
A frozen index has little overhead on the cluster and is blocked for write operations.
|
||||
You can search a frozen index, but expect queries to be slower.
|
||||
"
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
</div>
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
};
|
||||
oneIndexSelected = () => {
|
||||
return this.props.indexNames.length === 1;
|
||||
};
|
||||
getEntity = oneIndexSelected => {
|
||||
const { intl } = this.props;
|
||||
return oneIndexSelected ? (
|
||||
intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.indexMessage',
|
||||
defaultMessage: 'index'
|
||||
})
|
||||
) : (
|
||||
intl.formatMessage({
|
||||
id: 'xpack.idxMgmt.indexActionsMenu.indicesMessage',
|
||||
defaultMessage: 'indices'
|
||||
})
|
||||
);
|
||||
};
|
||||
render() {
|
||||
const { indexNames, intl } = this.props;
|
||||
const selectedIndexCount = indexNames.length;
|
||||
|
|
|
@ -13,7 +13,6 @@ import { healthToColor } from '../../../../services';
|
|||
import { REFRESH_RATE_INDEX_LIST } from '../../../../constants';
|
||||
|
||||
import {
|
||||
EuiBadge,
|
||||
EuiButton,
|
||||
EuiCallOut,
|
||||
EuiHealth,
|
||||
|
@ -44,8 +43,8 @@ import {
|
|||
getBannerExtensions,
|
||||
getFilterExtensions,
|
||||
getToggleExtensions,
|
||||
getBadgeExtensions,
|
||||
} from '../../../../index_management_extensions';
|
||||
import { renderBadges } from '../../../../lib/render_badges';
|
||||
|
||||
const HEADERS = {
|
||||
name: i18n.translate('xpack.idxMgmt.indexTable.headers.nameHeader', {
|
||||
|
@ -213,25 +212,6 @@ export class IndexTableUi extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
renderBadges(index) {
|
||||
const badgeLabels = [];
|
||||
getBadgeExtensions().forEach(({ matchIndex, label }) => {
|
||||
if (matchIndex(index)) {
|
||||
badgeLabels.push(label);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
{badgeLabels.map((badgeLabel) => {
|
||||
return (
|
||||
<Fragment key={badgeLabel}>
|
||||
{' '}<EuiBadge color="primary">{badgeLabel}</EuiBadge>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
buildRowCell(fieldName, value, index) {
|
||||
const { openDetailPanel } = this.props;
|
||||
if (fieldName === 'health') {
|
||||
|
@ -245,7 +225,7 @@ export class IndexTableUi extends Component {
|
|||
openDetailPanel(value);
|
||||
}}
|
||||
>
|
||||
{value}{this.renderBadges(index)}
|
||||
{value}{renderBadges(index)}
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,20 @@ export async function clearCacheIndices(indices) {
|
|||
const response = await httpClient.post(`${apiPrefix}/indices/clear_cache`, body);
|
||||
return response.data;
|
||||
}
|
||||
export async function freezeIndices(indices) {
|
||||
const body = {
|
||||
indices
|
||||
};
|
||||
const response = await httpClient.post(`${apiPrefix}/indices/freeze`, body);
|
||||
return response.data;
|
||||
}
|
||||
export async function unfreezeIndices(indices) {
|
||||
const body = {
|
||||
indices
|
||||
};
|
||||
const response = await httpClient.post(`${apiPrefix}/indices/unfreeze`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function loadIndexSettings(indexName) {
|
||||
const response = await httpClient.get(`${apiPrefix}/settings/${indexName}`);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createAction } from 'redux-actions';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { freezeIndices as request } from '../../services';
|
||||
import { clearRowStatus, reloadIndices } from '../actions';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
export const freezeIndicesStart = createAction(
|
||||
'INDEX_MANAGEMENT_FREEZE_INDICES_START'
|
||||
);
|
||||
|
||||
export const freezeIndices = ({ indexNames }) => async (dispatch) => {
|
||||
dispatch(freezeIndicesStart({ indexNames }));
|
||||
try {
|
||||
await request(indexNames);
|
||||
} catch (error) {
|
||||
toastNotifications.addDanger(error.data.message);
|
||||
return dispatch(clearRowStatus({ indexNames }));
|
||||
}
|
||||
dispatch(reloadIndices(indexNames));
|
||||
toastNotifications.addSuccess(
|
||||
i18n.translate('xpack.idxMgmt.freezeIndicesAction.successfullyFrozeIndicesMessage', {
|
||||
defaultMessage: 'Successfully froze: [{indexNames}]',
|
||||
values: { indexNames: indexNames.join(', ') }
|
||||
})
|
||||
);
|
||||
};
|
|
@ -14,6 +14,8 @@ export * from './load_indices';
|
|||
export * from './load_index_data';
|
||||
export * from './open_indices';
|
||||
export * from './refresh_indices';
|
||||
export * from './freeze_indices';
|
||||
export * from './unfreeze_indices';
|
||||
export * from './reload_indices';
|
||||
export * from './table_state';
|
||||
export * from './edit_index_settings';
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createAction } from 'redux-actions';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { unfreezeIndices as request } from '../../services';
|
||||
import { clearRowStatus, reloadIndices } from '../actions';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
export const unfreezeIndicesStart = createAction(
|
||||
'INDEX_MANAGEMENT_UNFREEZE_INDICES_START'
|
||||
);
|
||||
|
||||
export const unfreezeIndices = ({ indexNames }) => async (dispatch) => {
|
||||
dispatch(unfreezeIndicesStart({ indexNames }));
|
||||
try {
|
||||
await request(indexNames);
|
||||
} catch (error) {
|
||||
toastNotifications.addDanger(error.data.message);
|
||||
return dispatch(clearRowStatus({ indexNames }));
|
||||
}
|
||||
dispatch(reloadIndices(indexNames));
|
||||
toastNotifications.addSuccess(
|
||||
i18n.translate('xpack.idxMgmt.unfreezeIndicesAction.successfullyUnfrozeIndicesMessage', {
|
||||
defaultMessage: 'Successfully unfroze: [{indexNames}]',
|
||||
values: { indexNames: indexNames.join(', ') }
|
||||
})
|
||||
);
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getIndexManagementDataEnrichers } from '../../index_management_data';
|
||||
export const enrichResponse = async (response, callWithRequest) => {
|
||||
let enrichedResponse = response;
|
||||
const dataEnrichers = getIndexManagementDataEnrichers();
|
||||
for (let i = 0; i < dataEnrichers.length; i++) {
|
||||
const dataEnricher = dataEnrichers[i];
|
||||
try {
|
||||
const dataEnricherResponse = await dataEnricher(enrichedResponse, callWithRequest);
|
||||
enrichedResponse = dataEnricherResponse;
|
||||
} catch(e) {
|
||||
// silently swallow enricher response errors
|
||||
}
|
||||
}
|
||||
return enrichedResponse;
|
||||
};
|
68
x-pack/plugins/index_management/server/lib/fetch_indices.js
Normal file
68
x-pack/plugins/index_management/server/lib/fetch_indices.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { wrapEsError, wrapUnknownError } from './error_wrappers';
|
||||
import { fetchAliases } from './fetch_aliases';
|
||||
import { getIndexManagementDataEnrichers } from '../../index_management_data';
|
||||
|
||||
const enrichResponse = async (response, callWithRequest) => {
|
||||
let enrichedResponse = response;
|
||||
const dataEnrichers = getIndexManagementDataEnrichers();
|
||||
for (let i = 0; i < dataEnrichers.length; i++) {
|
||||
const dataEnricher = dataEnrichers[i];
|
||||
try {
|
||||
const dataEnricherResponse = await dataEnricher(enrichedResponse, callWithRequest);
|
||||
enrichedResponse = dataEnricherResponse;
|
||||
} catch(e) {
|
||||
// silently swallow enricher response errors
|
||||
}
|
||||
}
|
||||
return enrichedResponse;
|
||||
};
|
||||
function formatHits(hits, aliases) {
|
||||
return hits.map(hit => {
|
||||
return {
|
||||
health: hit.health,
|
||||
status: hit.status,
|
||||
name: hit.index,
|
||||
uuid: hit.uuid,
|
||||
primary: hit.pri,
|
||||
replica: hit.rep,
|
||||
documents: hit["docs.count"],
|
||||
size: hit["store.size"],
|
||||
isFrozen: hit.sth === 'true', // sth value coming back as a string from ES
|
||||
aliases: aliases.hasOwnProperty(hit.index) ? aliases[hit.index] : 'none',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchIndicesCall(callWithRequest, indexNames) {
|
||||
const params = {
|
||||
format: 'json',
|
||||
h: 'health,status,index,uuid,pri,rep,docs.count,sth,store.size'
|
||||
};
|
||||
if (indexNames) {
|
||||
params.index = indexNames;
|
||||
}
|
||||
|
||||
return await callWithRequest('cat.indices', params);
|
||||
}
|
||||
|
||||
export const fetchIndices = async (callWithRequest, isEsError, indexNames) => {
|
||||
try {
|
||||
const aliases = await fetchAliases(callWithRequest);
|
||||
const hits = await fetchIndicesCall(callWithRequest, indexNames);
|
||||
let response = formatHits(hits, aliases);
|
||||
response = await enrichResponse(response, callWithRequest);
|
||||
return response;
|
||||
} catch (err) {
|
||||
if (isEsError(err)) {
|
||||
throw wrapEsError(err);
|
||||
}
|
||||
|
||||
throw wrapUnknownError(err);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
|
||||
import { isEsErrorFactory } from '../../../lib/is_es_error_factory';
|
||||
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
|
||||
import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory';
|
||||
|
||||
function getIndexArrayFromPayload(payload) {
|
||||
return payload.indices || [];
|
||||
}
|
||||
|
||||
async function freezeIndices(callWithRequest, indices) {
|
||||
const params = {
|
||||
path: `/${indices.join(',')}/_freeze`,
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
return await callWithRequest('transport.request', params);
|
||||
}
|
||||
|
||||
export function registerFreezeRoute(server) {
|
||||
const isEsError = isEsErrorFactory(server);
|
||||
const licensePreRouting = licensePreRoutingFactory(server);
|
||||
|
||||
server.route({
|
||||
path: '/api/index_management/indices/freeze',
|
||||
method: 'POST',
|
||||
handler: async (request, h) => {
|
||||
const callWithRequest = callWithRequestFactory(server, request);
|
||||
const indices = getIndexArrayFromPayload(request.payload);
|
||||
|
||||
try {
|
||||
await freezeIndices(callWithRequest, indices);
|
||||
return h.response();
|
||||
} catch (err) {
|
||||
if (isEsError(err)) {
|
||||
throw wrapEsError(err);
|
||||
}
|
||||
|
||||
throw wrapUnknownError(err);
|
||||
}
|
||||
},
|
||||
config: {
|
||||
pre: [ licensePreRouting ]
|
||||
}
|
||||
});
|
||||
}
|
|
@ -14,6 +14,8 @@ import { registerRefreshRoute } from './register_refresh_route';
|
|||
import { registerReloadRoute } from './register_reload_route';
|
||||
import { registerDeleteRoute } from './register_delete_route';
|
||||
import { registerShardsRoute } from './register_shards_route';
|
||||
import { registerFreezeRoute } from './register_freeze_route';
|
||||
import { registerUnfreezeRoute } from './register_unfreeze_route';
|
||||
|
||||
export function registerIndicesRoutes(server) {
|
||||
registerClearCacheRoute(server);
|
||||
|
@ -26,4 +28,6 @@ export function registerIndicesRoutes(server) {
|
|||
registerReloadRoute(server);
|
||||
registerDeleteRoute(server);
|
||||
registerShardsRoute(server);
|
||||
registerFreezeRoute(server);
|
||||
registerUnfreezeRoute(server);
|
||||
}
|
||||
|
|
|
@ -6,36 +6,8 @@
|
|||
|
||||
import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
|
||||
import { isEsErrorFactory } from '../../../lib/is_es_error_factory';
|
||||
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
|
||||
import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory';
|
||||
import { fetchAliases } from './fetch_aliases';
|
||||
import { enrichResponse } from '../../../lib/enrich_response';
|
||||
|
||||
function formatHits(hits, aliases) {
|
||||
return hits.map(hit => {
|
||||
return {
|
||||
health: hit.health,
|
||||
status: hit.status,
|
||||
name: hit.index,
|
||||
uuid: hit.uuid,
|
||||
primary: hit.pri,
|
||||
replica: hit.rep,
|
||||
documents: hit["docs.count"],
|
||||
documents_deleted: hit["docs.deleted"],
|
||||
size: hit["store.size"],
|
||||
primary_size: hit["pri.store.size"],
|
||||
aliases: aliases.hasOwnProperty(hit.index) ? aliases[hit.index] : 'none',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchIndices(callWithRequest) {
|
||||
const params = {
|
||||
format: 'json'
|
||||
};
|
||||
|
||||
return await callWithRequest('cat.indices', params);
|
||||
}
|
||||
import { fetchIndices } from '../../../lib/fetch_indices';
|
||||
|
||||
export function registerListRoute(server) {
|
||||
const isEsError = isEsErrorFactory(server);
|
||||
|
@ -46,20 +18,7 @@ export function registerListRoute(server) {
|
|||
method: 'GET',
|
||||
handler: async (request) => {
|
||||
const callWithRequest = callWithRequestFactory(server, request);
|
||||
|
||||
try {
|
||||
const aliases = await fetchAliases(callWithRequest);
|
||||
const hits = await fetchIndices(callWithRequest);
|
||||
let response = formatHits(hits, aliases);
|
||||
response = await enrichResponse(response, callWithRequest);
|
||||
return response;
|
||||
} catch (err) {
|
||||
if (isEsError(err)) {
|
||||
throw wrapEsError(err);
|
||||
}
|
||||
|
||||
throw wrapUnknownError(err);
|
||||
}
|
||||
return fetchIndices(callWithRequest, isEsError);
|
||||
},
|
||||
config: {
|
||||
pre: [licensePreRouting]
|
||||
|
|
|
@ -6,42 +6,12 @@
|
|||
|
||||
import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
|
||||
import { isEsErrorFactory } from '../../../lib/is_es_error_factory';
|
||||
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
|
||||
import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory';
|
||||
import { enrichResponse } from '../../../lib/enrich_response';
|
||||
import { fetchAliases } from './fetch_aliases';
|
||||
|
||||
import { fetchIndices } from '../../../lib/fetch_indices';
|
||||
function getIndexNamesFromPayload(payload) {
|
||||
return payload.indexNames || [];
|
||||
}
|
||||
|
||||
function formatHits(hits, aliases) {
|
||||
return hits.map(hit => {
|
||||
return {
|
||||
health: hit.health,
|
||||
status: hit.status,
|
||||
name: hit.index,
|
||||
uuid: hit.uuid,
|
||||
primary: hit.pri,
|
||||
replica: hit.rep,
|
||||
documents: hit["docs.count"],
|
||||
documents_deleted: hit["docs.deleted"],
|
||||
size: hit["store.size"],
|
||||
primary_size: hit["pri.store.size"],
|
||||
aliases: aliases.hasOwnProperty(hit.index) ? aliases[hit.index] : 'none',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchIndices(callWithRequest, indexNames) {
|
||||
const params = {
|
||||
format: 'json',
|
||||
index: indexNames
|
||||
};
|
||||
|
||||
return await callWithRequest('cat.indices', params);
|
||||
}
|
||||
|
||||
export function registerReloadRoute(server) {
|
||||
const isEsError = isEsErrorFactory(server);
|
||||
const licensePreRouting = licensePreRoutingFactory(server);
|
||||
|
@ -52,20 +22,7 @@ export function registerReloadRoute(server) {
|
|||
handler: async (request) => {
|
||||
const callWithRequest = callWithRequestFactory(server, request);
|
||||
const indexNames = getIndexNamesFromPayload(request.payload);
|
||||
|
||||
try {
|
||||
const indices = await fetchIndices(callWithRequest, indexNames);
|
||||
const aliases = await fetchAliases(callWithRequest);
|
||||
let response = formatHits(indices, aliases);
|
||||
response = await enrichResponse(response, callWithRequest);
|
||||
return response;
|
||||
} catch (err) {
|
||||
if (isEsError(err)) {
|
||||
throw wrapEsError(err);
|
||||
}
|
||||
|
||||
throw wrapUnknownError(err);
|
||||
}
|
||||
return await fetchIndices(callWithRequest, isEsError, indexNames);
|
||||
},
|
||||
config: {
|
||||
pre: [ licensePreRouting ]
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
|
||||
import { isEsErrorFactory } from '../../../lib/is_es_error_factory';
|
||||
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
|
||||
import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory';
|
||||
|
||||
function getIndexArrayFromPayload(payload) {
|
||||
return payload.indices || [];
|
||||
}
|
||||
|
||||
async function unfreezeIndices(callWithRequest, indices) {
|
||||
const params = {
|
||||
path: `/${indices.join(',')}/_unfreeze`,
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
return await callWithRequest('transport.request', params);
|
||||
}
|
||||
|
||||
export function registerUnfreezeRoute(server) {
|
||||
const isEsError = isEsErrorFactory(server);
|
||||
const licensePreRouting = licensePreRoutingFactory(server);
|
||||
|
||||
server.route({
|
||||
path: '/api/index_management/indices/unfreeze',
|
||||
method: 'POST',
|
||||
handler: async (request, h) => {
|
||||
const callWithRequest = callWithRequestFactory(server, request);
|
||||
const indices = getIndexArrayFromPayload(request.payload);
|
||||
|
||||
try {
|
||||
await unfreezeIndices(callWithRequest, indices);
|
||||
return h.response();
|
||||
} catch (err) {
|
||||
if (isEsError(err)) {
|
||||
throw wrapEsError(err);
|
||||
}
|
||||
|
||||
throw wrapUnknownError(err);
|
||||
}
|
||||
},
|
||||
config: {
|
||||
pre: [ licensePreRouting ]
|
||||
}
|
||||
});
|
||||
}
|
|
@ -27,6 +27,7 @@ export const rollupBadgeExtension = {
|
|||
label: i18n.translate('xpack.rollupJobs.indexMgmtBadge.rollupLabel', {
|
||||
defaultMessage: 'Rollup',
|
||||
}),
|
||||
color: 'secondary'
|
||||
};
|
||||
|
||||
addBadgeExtension(rollupBadgeExtension);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue