mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[UA] Do not allow reindexing data_stream backing indices (and other enhancements) (#213755)
## Summary The PR brings the following improvements to the UA ES deprecations flows: * Remove the "Reindex" option for _Frozen index_ deprecations that are related to a data stream's backing index (backing indices cannot be alias-based, see [thread](https://elastic.slack.com/archives/C08A04N8XHV/p1741017934359239)). * Propagate `"isClosedIndex"` information to the UI side, so that we can show a warning message in the reindex flows flyouts. The [original functionality](https://github.com/elastic/kibana/pull/58890) got lost in the woods. * Propagate `"isClosedIndex", "isFrozenIndex", "isReadonly"` properties as part of the `correctiveAction` details ONLY for _index_settings migrations_, instead of doing it as top level properties for all migrations. These properties did not make for most migration types. * Big refactor in `migrations.ts` file (server side). The logic for obtaining, filtering and enriching ES deprecations had become quite fragmented and hard to read. @Bamieh @jloleysens please take a close look at this one. * Improve existing type definitions.
This commit is contained in:
parent
68195d3afb
commit
f2ae05909d
36 changed files with 517 additions and 406 deletions
|
@ -50544,12 +50544,12 @@
|
|||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.tryAgainLabel": "Réessayer",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexText": "L'opération de réindexation permet de transformer un index en index compatible. Elle copiera tous les documents existants dans un nouvel index, et supprimera l'ancien index. En fonction de la taille et des ressources, la réindexation peut prendre beaucoup de temps, et vos données seront en lecture seule jusqu'à la fin de la tâche.",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton": "Marquer en lecture seule",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.description": "Si vous n'en avez plus besoin, vous pouvez également supprimer l'index de {indexManagementLinkHtml}.",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.title": "Alternative: Supprimer l'index",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.description": "Dégelez cet index et marquez-le en lecture seule. Cela vous assurera que cet index restera compatible avec la version majeure suivante.",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.title": "Option 1 : Dégeler l'index",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.description": "Vous pouvez également réindexer les données sous un index compatible. Tous les documents existants seront copiés vers le nouvel index, et l'ancien index sera supprimé. En fonction de la taille de l'index et des ressources disponibles, l'opération de réindexation peut prendre un certain temps. Vos données seront en lecture seule jusqu'à la fin de la réindexation.",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.title": "Option 2 : Réindexer les données",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.description": "Si vous n'en avez plus besoin, vous pouvez également supprimer l'index de {indexManagementLinkHtml}.",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.title": "Option 3 : Supprimer l'index",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreezeIndexButton": "Dégeler",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.flyoutHeader": "Mettre à jour {index}",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.frozenCallout.reindexFrozenIndex": "Les index gelés ne seront plus compatibles après la mise à niveau. Ainsi, cet index sera transformé en index non gelé lors de l'opération de mise à jour. {docsLink}",
|
||||
|
|
|
@ -50503,12 +50503,12 @@
|
|||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.tryAgainLabel": "再試行",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexText": "再インデックス処理により、インデックスを互換性のある新しいインデックスに変換できます。この処理では、既存のすべてのドキュメントを新しいインデックスにコピーし、古いインデックスを削除します。サイズとリソースによっては、再インデックス化に時間がかかることがあり、ジョブが完了するまではデータが読み取り専用状態になります。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton": "読み込み専用に設定",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.description": "不要になった場合は、{indexManagementLinkHtml}からインデックスを削除することもできます。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.title": "代替:インデックスの削除",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.description": "このインデックスの状態をフローズンから変更し、読み取り専用にします。これにより、インデックスは次のメジャーバージョンでも互換性が保証されます。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.title": "オプション 1:インデックスのフローズン状態を解除",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.description": "あるいは、新しい互換性のあるインデックスにデータを再インデックス化することもできます。既存のすべてのドキュメントは新しいインデックスにコピーされ、古いインデックスは削除されます。インデックスの規模と利用可能なリソースによっては、再インデックス処理に時間がかかる場合があります。再インデックス処理が完了するまで、データは読み取り専用モードになります。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.title": "オプション 2:データを再インデックス化",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.description": "不要になった場合は、{indexManagementLinkHtml}からインデックスを削除することもできます。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.title": "オプション3:インデックスの削除",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreezeIndexButton": "フローズン状態を解除",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.flyoutHeader": "{index}を更新",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.frozenCallout.reindexFrozenIndex": "アップグレード後は、フローズンインデックスはサポートされなくなります。その結果、このインデックスは更新処理中に非フローズンインデックスに変換されます。{docsLink}",
|
||||
|
|
|
@ -50588,12 +50588,12 @@
|
|||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.tryAgainLabel": "重试",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexText": "借助重新索引操作,可以将索引转换为新的兼容索引。这会将所有现有文档复制到新索引中,然后删除旧版索引。根据大小和资源限制,重新索引可能需要很长时间,您的数据将处于只读状态,直到作业完成。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton": "标记为只读",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.description": "如果不再需要该索引,还可以将其从 {indexManagementLinkHtml} 中删除。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.title": "选择:删除索引",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.description": "取消冻结此索引并将其标记为只读。这将确保该索引仍然兼容下一主要版本。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.title": "选项 1:取消冻结索引",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.description": "或者,您可以将数据重新索引到新的兼容索引中。所有现有文档将复制到新索引中,并会删除旧版索引。根据索引大小和可用资源,重新索引操作可能需要一些时间。您的数据将处于只读模式,直到重新索引完成。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.title": "选项 2:为数据重建索引",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.description": "如果不再需要该索引,还可以将其从 {indexManagementLinkHtml} 中删除。",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.title": "选项 3:删除索引",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreezeIndexButton": "取消冻结",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.flyoutHeader": "更新 {index}",
|
||||
"xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.frozenCallout.reindexFrozenIndex": "升级后将不再支持已冻结索引。因此,会在更新操作期间将此索引转换为非冻结索引。{docsLink}",
|
||||
|
|
|
@ -35,6 +35,11 @@ export const MOCK_REINDEX_DEPRECATION: EnrichedDeprecationInfo = {
|
|||
index: 'reindex_index',
|
||||
correctiveAction: {
|
||||
type: 'reindex',
|
||||
metadata: {
|
||||
isClosedIndex: false,
|
||||
isFrozenIndex: false,
|
||||
isInDataStream: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ const defaultReindexStatusMeta: ReindexStatusResponse['meta'] = {
|
|||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
};
|
||||
|
||||
describe('Reindex deprecation flyout', () => {
|
||||
|
|
|
@ -56,6 +56,7 @@ export interface ReindexStatusResponse {
|
|||
aliases: string[];
|
||||
isReadonly: boolean;
|
||||
isFrozen: boolean;
|
||||
isInDataStream: boolean;
|
||||
};
|
||||
warnings?: IndexWarning[];
|
||||
reindexOp?: ReindexOperation;
|
||||
|
@ -193,16 +194,22 @@ export interface DeprecationInfo {
|
|||
export interface IndexSettingsDeprecationInfo {
|
||||
[indexName: string]: DeprecationInfo[];
|
||||
}
|
||||
export interface ReindexAction {
|
||||
type: 'reindex';
|
||||
|
||||
export interface IndexMetadata {
|
||||
isFrozenIndex: boolean;
|
||||
isInDataStream: boolean;
|
||||
isClosedIndex: boolean;
|
||||
}
|
||||
|
||||
export interface IndexAction {
|
||||
/**
|
||||
* Indicate what blockers have been detected for calling reindex
|
||||
* against this index.
|
||||
*
|
||||
* @remark
|
||||
* In future this could be an array of blockers.
|
||||
* Includes relevant information about the index related to this action
|
||||
*/
|
||||
blockerForReindexing?: 'index-closed'; // 'index-closed' can be handled automatically, but requires more resources, user should be warned
|
||||
metadata: IndexMetadata;
|
||||
}
|
||||
|
||||
export interface ReindexAction extends IndexAction {
|
||||
type: 'reindex';
|
||||
|
||||
/**
|
||||
* The transform IDs that are currently targeting this index
|
||||
|
@ -215,7 +222,7 @@ export interface ReindexAction {
|
|||
excludedActions?: string[];
|
||||
}
|
||||
|
||||
export interface UnfreezeAction {
|
||||
export interface UnfreezeAction extends IndexAction {
|
||||
type: 'unfreeze';
|
||||
}
|
||||
|
||||
|
@ -242,6 +249,15 @@ export interface HealthIndicatorAction {
|
|||
impacts: HealthReportImpact[];
|
||||
}
|
||||
|
||||
export type CorrectiveAction =
|
||||
| ReindexAction
|
||||
| UnfreezeAction
|
||||
| MlAction
|
||||
| IndexSettingAction
|
||||
| ClusterSettingAction
|
||||
| DataStreamsAction
|
||||
| HealthIndicatorAction;
|
||||
|
||||
export interface EnrichedDeprecationInfo
|
||||
extends Omit<
|
||||
estypes.MigrationDeprecationsDeprecation,
|
||||
|
@ -256,14 +272,7 @@ export interface EnrichedDeprecationInfo
|
|||
isCritical: boolean;
|
||||
status?: estypes.HealthReportIndicatorHealthStatus;
|
||||
index?: string;
|
||||
correctiveAction?:
|
||||
| ReindexAction
|
||||
| UnfreezeAction
|
||||
| MlAction
|
||||
| IndexSettingAction
|
||||
| ClusterSettingAction
|
||||
| DataStreamsAction
|
||||
| HealthIndicatorAction;
|
||||
correctiveAction?: CorrectiveAction;
|
||||
resolveDuringUpgrade: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,12 @@ export const DurationClarificationCallOut: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
<br />
|
||||
<br />
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.backingIndicesUnfrozen"
|
||||
defaultMessage="If any of the backing indices of the data stream are frozen, they will be converted to non-frozen indices during the update process."
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.suggestReadOnly"
|
||||
defaultMessage="Depending on size and resources, reindexing may take extended time and your data will be in a read-only state until the job has completed. {learnMoreHtml}"
|
||||
|
|
|
@ -10,7 +10,7 @@ import React, { createContext, useContext } from 'react';
|
|||
import { ApiService } from '../../../../lib/api';
|
||||
import { useReindex, ReindexState } from './use_reindex';
|
||||
import { UpdateIndexState, useUpdateIndex } from './use_update_index';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { EnrichedDeprecationInfo, IndexAction } from '../../../../../../common/types';
|
||||
|
||||
export interface IndexStateContext {
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
|
@ -43,9 +43,13 @@ export const IndexStatusProvider: React.FunctionComponent<Props> = ({
|
|||
children,
|
||||
}) => {
|
||||
const indexName = deprecation.index!;
|
||||
const indexAction = deprecation.correctiveAction as IndexAction;
|
||||
const { reindexState, startReindex, cancelReindex } = useReindex({
|
||||
indexName,
|
||||
api,
|
||||
isInDataStream: Boolean(indexAction?.metadata.isInDataStream),
|
||||
isFrozen: Boolean(indexAction?.metadata.isFrozenIndex),
|
||||
isClosedIndex: Boolean(indexAction?.metadata.isClosedIndex),
|
||||
});
|
||||
|
||||
const { updateIndexState, updateIndex } = useUpdateIndex({
|
||||
|
|
|
@ -126,8 +126,6 @@ export const IndexFlyout: React.FunctionComponent<IndexFlyoutProps> = ({
|
|||
switch (flyoutStep) {
|
||||
case 'details':
|
||||
return correctiveAction?.type === 'unfreeze' ? (
|
||||
// we will show specific unfreeze details/flow for:
|
||||
// A) 7.x indices that are frozen AND read-only (should be an edge case)
|
||||
<UnfreezeDetailsFlyoutStep
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={() => {
|
||||
|
@ -141,11 +139,6 @@ export const IndexFlyout: React.FunctionComponent<IndexFlyoutProps> = ({
|
|||
reindexState={reindexState}
|
||||
/>
|
||||
) : (
|
||||
// we will show specific reindex details/flow for:
|
||||
// B) 7.x indices that are frozen AND NOT read-only (should be the most common scenario)
|
||||
// C) 7.x indices that are not frozen
|
||||
// C.1) if they are read-only => this will be a WARNING deprecation
|
||||
// C.2) if they are NOT read-only => this will be a CRITICAL deprecation
|
||||
<ReindexDetailsFlyoutStep
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={() => {
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ReindexStatus } from '../../../../../../../../../common/types';
|
||||
import { IndexClosedParagraph } from '../index_closed_paragraph';
|
||||
|
||||
export const getReindexButtonLabel = (status?: ReindexStatus) => {
|
||||
switch (status) {
|
||||
|
@ -47,11 +48,13 @@ export const getReindexButtonLabel = (status?: ReindexStatus) => {
|
|||
};
|
||||
|
||||
export const getDefaultGuideanceText = ({
|
||||
isClosedIndex,
|
||||
readOnlyExcluded,
|
||||
reindexExcluded,
|
||||
indexBlockUrl,
|
||||
indexManagementUrl,
|
||||
}: {
|
||||
isClosedIndex: boolean;
|
||||
readOnlyExcluded: boolean;
|
||||
reindexExcluded: boolean;
|
||||
indexBlockUrl: string;
|
||||
|
@ -73,6 +76,12 @@ export const getDefaultGuideanceText = ({
|
|||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option1.description"
|
||||
defaultMessage="The reindex operation allows transforming an index into a new, compatible one. It will copy all of the existing documents into a new index and remove the old one. Depending on size and resources, reindexing may take extended time and your data will be in a read-only state until the job has completed."
|
||||
/>
|
||||
{isClosedIndex && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="xs" />
|
||||
<IndexClosedParagraph />
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiText>
|
||||
),
|
||||
});
|
||||
|
|
|
@ -49,10 +49,12 @@ describe('ReindexDetailsFlyoutStep', () => {
|
|||
loadingState: LoadingState.Success,
|
||||
meta: {
|
||||
indexName: 'some_index',
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
aliases: [],
|
||||
isInDataStream: false,
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
isClosedIndex: false,
|
||||
},
|
||||
hasRequiredPrivileges: true,
|
||||
reindexTaskPercComplete: null,
|
||||
|
@ -227,7 +229,15 @@ describe('ReindexDetailsFlyoutStep', () => {
|
|||
updateIndexState={defaultUpdateIndexState()}
|
||||
deprecation={{
|
||||
...defaultDeprecation(),
|
||||
correctiveAction: { type: 'reindex', transformIds: ['abc', 'def'] },
|
||||
correctiveAction: {
|
||||
type: 'reindex',
|
||||
transformIds: ['abc', 'def'],
|
||||
metadata: {
|
||||
isFrozenIndex: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -239,6 +249,11 @@ describe('ReindexDetailsFlyoutStep', () => {
|
|||
deprecation={
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"transformIds": Array [
|
||||
"abc",
|
||||
"def",
|
||||
|
|
|
@ -36,6 +36,7 @@ import { FetchFailedCallOut } from '../fetch_failed_callout';
|
|||
import { ReindexingFailedCallOut } from '../reindexing_failed_callout';
|
||||
import { MlAnomalyGuidance } from './ml_anomaly_guidance';
|
||||
import { ESTransformsTargetGuidance } from './es_transform_target_guidance';
|
||||
import { IndexClosedParagraph } from '../index_closed_paragraph';
|
||||
|
||||
const ML_ANOMALIES_PREFIX = '.ml-anomalies-';
|
||||
|
||||
|
@ -66,7 +67,7 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
|||
|
||||
const { loadingState, status: reindexStatus, hasRequiredPrivileges, meta } = reindexState;
|
||||
const { status: updateIndexStatus } = updateIndexState;
|
||||
const { indexName } = meta;
|
||||
const { indexName, isFrozen, isClosedIndex, isReadonly } = meta;
|
||||
const loading = loadingState === LoadingState.Loading;
|
||||
const isCompleted = reindexStatus === ReindexStatus.completed || updateIndexStatus === 'complete';
|
||||
const hasFetchFailed = reindexStatus === ReindexStatus.fetchFailed;
|
||||
|
@ -87,7 +88,7 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
|||
|
||||
if (isESTransformTarget) {
|
||||
showEsTransformsGuidance = true;
|
||||
} else if (meta.isReadonly) {
|
||||
} else if (isReadonly) {
|
||||
showReadOnlyGuidance = true;
|
||||
} else if (isMLAnomalyIndex) {
|
||||
showMlAnomalyReindexingGuidance = true;
|
||||
|
@ -161,7 +162,7 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
|||
<ReindexingFailedCallOut errorMessage={reindexState.errorMessage!} />
|
||||
)}
|
||||
|
||||
{meta.isFrozen && <FrozenCallOut />}
|
||||
{isFrozen && <FrozenCallOut />}
|
||||
|
||||
<EuiText>
|
||||
{showEsTransformsGuidance && <ESTransformsTargetGuidance deprecation={deprecation} />}
|
||||
|
@ -180,6 +181,11 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
|||
defaultMessage="The reindex operation allows transforming an index into a new, compatible one. It will copy all of the existing documents into a new index and remove the old one. Depending on size and resources, reindexing may take extended time and your data will be in a read-only state until the job has completed."
|
||||
/>
|
||||
</p>
|
||||
{isClosedIndex && (
|
||||
<p>
|
||||
<IndexClosedParagraph />
|
||||
</p>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{showDefaultGuidance && (
|
||||
|
@ -193,6 +199,7 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
|||
<EuiDescriptionList
|
||||
rowGutterSize="m"
|
||||
listItems={getDefaultGuideanceText({
|
||||
isClosedIndex,
|
||||
readOnlyExcluded,
|
||||
reindexExcluded,
|
||||
indexManagementUrl: `${http.basePath.prepend(
|
||||
|
@ -223,7 +230,7 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
{!meta.isReadonly &&
|
||||
{!isReadonly &&
|
||||
!hasFetchFailed &&
|
||||
!isCompleted &&
|
||||
hasRequiredPrivileges &&
|
||||
|
|
|
@ -44,6 +44,8 @@ describe('UnfreezeDetailsFlyoutStep', () => {
|
|||
aliases: [],
|
||||
isFrozen: true,
|
||||
isReadonly: true,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
},
|
||||
hasRequiredPrivileges: true,
|
||||
|
@ -56,7 +58,7 @@ describe('UnfreezeDetailsFlyoutStep', () => {
|
|||
failedBefore: false,
|
||||
};
|
||||
|
||||
it('renders', () => {
|
||||
it('renders all options for regular indices', () => {
|
||||
const wrapper = shallow(
|
||||
<UnfreezeDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
|
@ -67,132 +69,30 @@ describe('UnfreezeDetailsFlyoutStep', () => {
|
|||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="This index is frozen. Frozen indices will no longer be supported after the upgrade. Choose one of the following options:"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.frozenIndexText"
|
||||
/>
|
||||
</p>
|
||||
<EuiDescriptionList
|
||||
listItems={
|
||||
Array [
|
||||
Object {
|
||||
"description": <EuiText
|
||||
size="m"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Unfreeze this index and make it read-only. This ensures that the index will remain compatible with the next major version."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.description"
|
||||
/>
|
||||
</EuiText>,
|
||||
"title": "Option 1: Unfreeze index",
|
||||
},
|
||||
Object {
|
||||
"description": <EuiText
|
||||
size="m"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Alternatively, you can reindex the data into a new, compatible index. All existing documents will be copied over to a new index, and the old index will be removed. Depending on the size of the index and the available resources, the reindexing operation can take some time. Your data will be in read-only mode until the reindexing has completed."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.description"
|
||||
/>
|
||||
</EuiText>,
|
||||
"title": "Option 2: Reindex data",
|
||||
},
|
||||
Object {
|
||||
"description": <EuiText
|
||||
size="m"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="If you no longer need it, you can also delete the index from {indexManagementLinkHtml}."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.description"
|
||||
values={
|
||||
Object {
|
||||
"indexManagementLinkHtml": <EuiLink
|
||||
href="undefined"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Index Management"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.indexMgmtLink"
|
||||
/>
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>,
|
||||
"title": "Option 3: Delete index",
|
||||
},
|
||||
]
|
||||
}
|
||||
rowGutterSize="m"
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup
|
||||
justifyContent="spaceBetween"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
flush="left"
|
||||
iconType="cross"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiFlexGroup
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="startReindexingButton"
|
||||
disabled={false}
|
||||
isLoading={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
disabled={false}
|
||||
fill={true}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
defaultMessage="Unfreeze"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreezeIndexButton"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
expect(wrapper.find('EuiButton[data-test-subj="startReindexingButton"]')).toHaveLength(1);
|
||||
expect(wrapper.find('EuiButton[data-test-subj="startUnfreezeButton"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does NOT render Reindex option for data stream backing indices', () => {
|
||||
const backingIndexReindexState = {
|
||||
...defaultReindexState,
|
||||
meta: {
|
||||
...defaultReindexState.meta,
|
||||
isInDataStream: true,
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<UnfreezeDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
startReindex={jest.fn()}
|
||||
unfreeze={jest.fn()}
|
||||
reindexState={backingIndexReindexState}
|
||||
updateIndexState={defaultUpdateIndexState}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper.find('EuiButton[data-test-subj="startReindexingButton"]')).toHaveLength(0);
|
||||
expect(wrapper.find('EuiButton[data-test-subj="startUnfreezeButton"]')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,6 +31,7 @@ import { getReindexButtonLabel } from './messages';
|
|||
import type { UpdateIndexState } from '../../../use_update_index';
|
||||
import { FetchFailedCallOut } from '../fetch_failed_callout';
|
||||
import { ReindexingFailedCallOut } from '../reindexing_failed_callout';
|
||||
import { IndexClosedParagraph } from '../index_closed_paragraph';
|
||||
|
||||
/**
|
||||
* Displays a flyout that shows the details / corrective action for a "reindex" deprecation for a given index.
|
||||
|
@ -51,7 +52,7 @@ export const UnfreezeDetailsFlyoutStep: React.FunctionComponent<{
|
|||
|
||||
const { loadingState, status: reindexStatus, hasRequiredPrivileges, meta } = reindexState;
|
||||
const { status: updateIndexStatus } = updateIndexState;
|
||||
const { indexName } = meta;
|
||||
const { indexName, isInDataStream, isClosedIndex } = meta;
|
||||
const loading = loadingState === LoadingState.Loading;
|
||||
const isCompleted = reindexStatus === ReindexStatus.completed || updateIndexStatus === 'complete';
|
||||
const hasFetchFailed = reindexStatus === ReindexStatus.fetchFailed;
|
||||
|
@ -151,33 +152,44 @@ export const UnfreezeDetailsFlyoutStep: React.FunctionComponent<{
|
|||
</EuiText>
|
||||
),
|
||||
},
|
||||
/* We cannot reindex backing indices in the same way as regular indices (that would break the related data_stream) */
|
||||
...(!isInDataStream
|
||||
? [
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.title',
|
||||
{
|
||||
defaultMessage: 'Option 2: Reindex data',
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.description"
|
||||
defaultMessage="Alternatively, you can reindex the data into a new, compatible index. All existing documents will be copied over to a new index, and the old index will be removed. Depending on the size of the index and the available resources, the reindexing operation can take some time. Your data will be in read-only mode until the reindexing has completed."
|
||||
/>
|
||||
{isClosedIndex && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="xs" />
|
||||
<IndexClosedParagraph />
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.title',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.title',
|
||||
{
|
||||
defaultMessage: 'Option 2: Reindex data',
|
||||
defaultMessage: 'Alternative: Delete the index',
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option2.description"
|
||||
defaultMessage="Alternatively, you can reindex the data into a new, compatible index. All existing documents will be copied over to a new index, and the old index will be removed. Depending on the size of the index and the available resources, the reindexing operation can take some time. Your data will be in read-only mode until the reindexing has completed."
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.title',
|
||||
{
|
||||
defaultMessage: 'Option 3: Delete index',
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option3.description"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.alternativeOption.description"
|
||||
defaultMessage="If you no longer need it, you can also delete the index from {indexManagementLinkHtml}."
|
||||
values={{
|
||||
indexManagementLinkHtml: (
|
||||
|
@ -214,7 +226,8 @@ export const UnfreezeDetailsFlyoutStep: React.FunctionComponent<{
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
{!hasFetchFailed && !isCompleted && hasRequiredPrivileges && (
|
||||
{/* We cannot reindex backing indices in the same way as regular indices (that would break the related data_stream) */}
|
||||
{!isInDataStream && !hasFetchFailed && !isCompleted && hasRequiredPrivileges && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color={reindexStatus === ReindexStatus.cancelled ? 'warning' : 'primary'}
|
||||
|
@ -234,12 +247,11 @@ export const UnfreezeDetailsFlyoutStep: React.FunctionComponent<{
|
|||
fill
|
||||
onClick={unfreeze}
|
||||
disabled={loading}
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
data-test-subj="startUnfreezeButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreezeIndexButton"
|
||||
defaultMessage="Unfreeze"
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useAppContext } from '../../../../../../app_context';
|
||||
|
||||
export const IndexClosedParagraph: React.FunctionComponent = () => {
|
||||
const {
|
||||
services: {
|
||||
core: { docLinks },
|
||||
},
|
||||
} = useAppContext();
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.indexClosed"
|
||||
defaultMessage="This index is currently closed. The Upgrade Assistant will open, reindex and then close the index. {reindexingMayTakeLongerEmph}. {learnMore}"
|
||||
values={{
|
||||
learnMore: (
|
||||
<EuiLink target="_blank" href={docLinks.links.apis.openIndex}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.learnMoreLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Learn more',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
reindexingMayTakeLongerEmph: (
|
||||
<b>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexTakesLonger',
|
||||
{ defaultMessage: 'Reindexing may take longer than usual' }
|
||||
)}
|
||||
</b>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -39,7 +39,9 @@ exports[`ReindexStep renders 1`] = `
|
|||
"meta": Object {
|
||||
"aliases": Array [],
|
||||
"indexName": "myIndex",
|
||||
"isClosedIndex": false,
|
||||
"isFrozen": false,
|
||||
"isInDataStream": false,
|
||||
"isReadonly": false,
|
||||
"reindexName": "reindexed-myIndex",
|
||||
},
|
||||
|
@ -130,7 +132,9 @@ exports[`ReindexStep renders for frozen indices 1`] = `
|
|||
"meta": Object {
|
||||
"aliases": Array [],
|
||||
"indexName": "myIndex",
|
||||
"isClosedIndex": false,
|
||||
"isFrozen": true,
|
||||
"isInDataStream": false,
|
||||
"isReadonly": false,
|
||||
"reindexName": "reindexed-myIndex",
|
||||
},
|
||||
|
|
|
@ -30,6 +30,8 @@ describe('ReindexProgress', () => {
|
|||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
} as ReindexState
|
||||
}
|
||||
|
@ -98,7 +100,9 @@ describe('ReindexProgress', () => {
|
|||
"meta": Object {
|
||||
"aliases": Array [],
|
||||
"indexName": "foo",
|
||||
"isClosedIndex": false,
|
||||
"isFrozen": false,
|
||||
"isInDataStream": false,
|
||||
"isReadonly": false,
|
||||
"reindexName": "reindexed-foo",
|
||||
},
|
||||
|
@ -179,6 +183,8 @@ describe('ReindexProgress', () => {
|
|||
aliases: [],
|
||||
isFrozen: true,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
} as ReindexState
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ describe('ReindexStep', () => {
|
|||
aliases: [],
|
||||
isReadonly: false,
|
||||
isFrozen: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
} as ReindexState,
|
||||
};
|
||||
|
|
|
@ -15,8 +15,10 @@ describe('UpdateIndexFlyoutStep', () => {
|
|||
const meta: ReindexState['meta'] = {
|
||||
indexName: 'some_index',
|
||||
aliases: [],
|
||||
isInDataStream: false,
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isClosedIndex: false,
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
};
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ describe('WarningFlyoutStep', () => {
|
|||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -33,14 +33,16 @@ export interface ReindexState {
|
|||
aliases: string[];
|
||||
isFrozen: boolean;
|
||||
isReadonly: boolean;
|
||||
isInDataStream: boolean;
|
||||
isClosedIndex: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const getReindexState = (
|
||||
reindexState: ReindexState,
|
||||
{ reindexOp, warnings, hasRequiredPrivileges, meta: updatedMeta }: ReindexStatusResponse
|
||||
) => {
|
||||
const meta = { ...(updatedMeta ?? reindexState.meta) };
|
||||
): ReindexState => {
|
||||
const meta = { ...reindexState.meta, ...updatedMeta };
|
||||
// Once we have received an array of existing aliases, we won't update the meta value anymore because
|
||||
// when we'll delete the original alias during the reindex process there won't be any aliases pointing
|
||||
// to it anymore and the last reindex step (Update existing aliases) would be suddenly removed.
|
||||
|
@ -108,7 +110,19 @@ const getReindexState = (
|
|||
return newReindexState;
|
||||
};
|
||||
|
||||
export const useReindex = ({ indexName, api }: { indexName: string; api: ApiService }) => {
|
||||
export const useReindex = ({
|
||||
indexName,
|
||||
isFrozen,
|
||||
isInDataStream,
|
||||
isClosedIndex,
|
||||
api,
|
||||
}: {
|
||||
indexName: string;
|
||||
isFrozen: boolean;
|
||||
isInDataStream: boolean;
|
||||
isClosedIndex: boolean;
|
||||
api: ApiService;
|
||||
}) => {
|
||||
const [reindexState, setReindexState] = useState<ReindexState>({
|
||||
loadingState: LoadingState.Loading,
|
||||
errorMessage: null,
|
||||
|
@ -118,8 +132,10 @@ export const useReindex = ({ indexName, api }: { indexName: string; api: ApiServ
|
|||
// these properties will be known after fetching the reindexStatus
|
||||
reindexName: '',
|
||||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isFrozen,
|
||||
isInDataStream,
|
||||
isClosedIndex,
|
||||
isReadonly: false, // we don't have this information in the deprecation list
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { UpdateIndexOperation } from '../../../../../../common/update_index';
|
||||
import type { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import type { CorrectiveAction } from '../../../../../../common/types';
|
||||
import type { ApiService } from '../../../../lib/api';
|
||||
|
||||
export interface UpdateIndexState {
|
||||
|
@ -19,7 +19,7 @@ export interface UpdateIndexState {
|
|||
export interface UseUpdateIndexParams {
|
||||
indexName: string;
|
||||
api: ApiService;
|
||||
correctiveAction: EnrichedDeprecationInfo['correctiveAction'];
|
||||
correctiveAction?: CorrectiveAction;
|
||||
}
|
||||
|
||||
export const useUpdateIndex = ({ indexName, api, correctiveAction }: UseUpdateIndexParams) => {
|
||||
|
|
|
@ -80,8 +80,12 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": undefined,
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "reindex",
|
||||
},
|
||||
"details": "This index was created using version: 6.8.13",
|
||||
|
@ -94,22 +98,29 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": undefined,
|
||||
"excludedActions": Array [],
|
||||
"type": "reindex",
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": true,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "unfreeze",
|
||||
},
|
||||
"details": "This index has version: 7.17.28-8.0.0",
|
||||
"details": "Frozen indices must be unfrozen before upgrading to version 9.0. (The legacy frozen indices feature no longer offers any advantages. You may consider cold or frozen tiers in place of frozen indices.)",
|
||||
"index": "frozen_index",
|
||||
"isCritical": true,
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"message": "Index [frozen_index] is a frozen index. The frozen indices feature is deprecated and will be removed in version 9.0.",
|
||||
"resolveDuringUpgrade": false,
|
||||
"type": "index_settings",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.0.html#breaking-changes-8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/frozen-indices.html",
|
||||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": "index-closed",
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": true,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "reindex",
|
||||
},
|
||||
"details": "This index was created using version: 6.8.13",
|
||||
|
@ -177,8 +188,12 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": undefined,
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"transformIds": Array [
|
||||
"abc",
|
||||
],
|
||||
|
@ -194,8 +209,12 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": undefined,
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "reindex",
|
||||
},
|
||||
"details": "This index has version: 7.17.25",
|
||||
|
|
|
@ -5,32 +5,30 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EnrichedDeprecationInfo } from '../../../common/types';
|
||||
import type { CorrectiveAction } from '../../../common/types';
|
||||
import type { BaseDeprecation } from './migrations';
|
||||
|
||||
interface Action {
|
||||
action_type: 'remove_settings';
|
||||
objects: string[];
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
interface CommonActionMetadata {
|
||||
actions?: Action[];
|
||||
}
|
||||
|
||||
interface MlActionMetadata {
|
||||
actions?: Action[];
|
||||
interface MlActionMetadata extends CommonActionMetadata {
|
||||
snapshot_id: string;
|
||||
job_id: string;
|
||||
}
|
||||
|
||||
interface IndexActionMetadata {
|
||||
actions?: Action[];
|
||||
interface IndexActionMetadata extends CommonActionMetadata {
|
||||
reindex_required: boolean;
|
||||
transform_ids: string[];
|
||||
is_in_data_stream?: boolean;
|
||||
}
|
||||
|
||||
interface DataStreamActionMetadata {
|
||||
actions?: Action[];
|
||||
|
||||
interface DataStreamActionMetadata extends CommonActionMetadata {
|
||||
excludedActions?: Array<'readOnly' | 'reindex'>;
|
||||
total_backing_indices: number;
|
||||
reindex_required: boolean;
|
||||
|
@ -44,7 +42,7 @@ interface DataStreamActionMetadata {
|
|||
ignored_indices_requiring_upgrade_count?: number;
|
||||
}
|
||||
|
||||
export type EsMetadata = Actions | MlActionMetadata | DataStreamActionMetadata;
|
||||
export type EsMetadata = IndexActionMetadata | MlActionMetadata | DataStreamActionMetadata;
|
||||
|
||||
// TODO(jloleysens): Replace these regexes once this issue is addressed https://github.com/elastic/elasticsearch/issues/118062
|
||||
const ES_INDEX_MESSAGES_REQUIRING_REINDEX = [
|
||||
|
@ -55,26 +53,23 @@ const ES_INDEX_MESSAGES_REQUIRING_REINDEX = [
|
|||
export const isFrozenDeprecation = (message: string, indexName?: string): boolean =>
|
||||
Boolean(indexName) && message.includes(`Index [${indexName}] is a frozen index`);
|
||||
|
||||
export const getCorrectiveAction = (
|
||||
deprecationType: EnrichedDeprecationInfo['type'],
|
||||
message: string,
|
||||
metadata: EsMetadata,
|
||||
indexName?: string
|
||||
): EnrichedDeprecationInfo['correctiveAction'] => {
|
||||
export const getCorrectiveAction = (deprecation: BaseDeprecation): CorrectiveAction | undefined => {
|
||||
const { index, type, message, metadata } = deprecation;
|
||||
|
||||
const indexSettingDeprecation = metadata?.actions?.find(
|
||||
(action) => action.action_type === 'remove_settings' && indexName
|
||||
(action) => action.action_type === 'remove_settings' && index
|
||||
);
|
||||
const clusterSettingDeprecation = metadata?.actions?.find(
|
||||
(action) => action.action_type === 'remove_settings' && typeof indexName === 'undefined'
|
||||
(action) => action.action_type === 'remove_settings' && typeof index === 'undefined'
|
||||
);
|
||||
const requiresReindexAction = ES_INDEX_MESSAGES_REQUIRING_REINDEX.some((regexp) =>
|
||||
regexp.test(message)
|
||||
);
|
||||
const requiresUnfreezeAction = isFrozenDeprecation(message, indexName);
|
||||
const requiresUnfreezeAction = isFrozenDeprecation(message, index);
|
||||
const requiresIndexSettingsAction = Boolean(indexSettingDeprecation);
|
||||
const requiresClusterSettingsAction = Boolean(clusterSettingDeprecation);
|
||||
const requiresMlAction = /[Mm]odel snapshot/.test(message);
|
||||
const requiresDataStreamsAction = deprecationType === 'data_streams';
|
||||
const requiresDataStreamsAction = type === 'data_streams';
|
||||
|
||||
if (requiresDataStreamsAction) {
|
||||
const {
|
||||
|
@ -114,12 +109,22 @@ export const getCorrectiveAction = (
|
|||
return {
|
||||
type: 'reindex',
|
||||
...(transformIds?.length ? { transformIds } : {}),
|
||||
metadata: {
|
||||
isClosedIndex: Boolean(deprecation.isClosedIndex),
|
||||
isFrozenIndex: Boolean(deprecation.isFrozenIndex),
|
||||
isInDataStream: Boolean((deprecation.metadata as IndexActionMetadata)?.is_in_data_stream),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (requiresUnfreezeAction) {
|
||||
return {
|
||||
type: 'unfreeze',
|
||||
metadata: {
|
||||
isClosedIndex: Boolean(deprecation.isClosedIndex),
|
||||
isFrozenIndex: Boolean(deprecation.isFrozenIndex),
|
||||
isInDataStream: Boolean((deprecation.metadata as IndexActionMetadata)?.is_in_data_stream),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,18 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { elasticsearchServiceMock, ScopedClusterClientMock } from '@kbn/core/server/mocks';
|
||||
import { elasticsearchServiceMock, type ElasticsearchClientMock } from '@kbn/core/server/mocks';
|
||||
import { getHealthIndicators } from './health_indicators';
|
||||
import * as healthIndicatorsMock from '../__fixtures__/health_indicators';
|
||||
|
||||
describe('getHealthIndicators', () => {
|
||||
let esClient: ScopedClusterClientMock;
|
||||
let esClient: ElasticsearchClientMock;
|
||||
beforeEach(() => {
|
||||
esClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
|
||||
});
|
||||
|
||||
it('returns empty array on green indicators', async () => {
|
||||
esClient.asCurrentUser.healthReport.mockResponse({
|
||||
esClient.healthReport.mockResponse({
|
||||
cluster_name: 'mock',
|
||||
indicators: {
|
||||
disk: healthIndicatorsMock.diskIndicatorGreen,
|
||||
|
@ -30,7 +30,7 @@ describe('getHealthIndicators', () => {
|
|||
});
|
||||
|
||||
it('returns unknown indicators', async () => {
|
||||
esClient.asCurrentUser.healthReport.mockResponse({
|
||||
esClient.healthReport.mockResponse({
|
||||
cluster_name: 'mock',
|
||||
indicators: {
|
||||
disk: healthIndicatorsMock.diskIndicatorUnknown,
|
||||
|
@ -48,7 +48,7 @@ describe('getHealthIndicators', () => {
|
|||
});
|
||||
|
||||
it('returns unhealthy shards_capacity indicator', async () => {
|
||||
esClient.asCurrentUser.healthReport.mockResponse({
|
||||
esClient.healthReport.mockResponse({
|
||||
cluster_name: 'mock',
|
||||
indicators: {
|
||||
disk: healthIndicatorsMock.diskIndicatorGreen,
|
||||
|
@ -92,7 +92,7 @@ describe('getHealthIndicators', () => {
|
|||
});
|
||||
|
||||
it('returns unhealthy disk indicator', async () => {
|
||||
esClient.asCurrentUser.healthReport.mockResponse({
|
||||
esClient.healthReport.mockResponse({
|
||||
cluster_name: 'mock',
|
||||
indicators: {
|
||||
disk: healthIndicatorsMock.diskIndicatorRed,
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/types';
|
||||
import { IScopedClusterClient } from '@kbn/core/server';
|
||||
import { EnrichedDeprecationInfo } from '../../../common/types';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import type { EnrichedDeprecationInfo } from '../../../common/types';
|
||||
|
||||
export async function getHealthIndicators(
|
||||
dataClient: IScopedClusterClient
|
||||
dataClient: ElasticsearchClient
|
||||
): Promise<EnrichedDeprecationInfo[]> {
|
||||
const healthIndicators = await dataClient.asCurrentUser.healthReport();
|
||||
const healthIndicators = await dataClient.healthReport();
|
||||
const isStatusNotGreen = (indicator?: estypes.HealthReportBaseIndicator): boolean => {
|
||||
return !!(indicator?.status && indicator?.status !== 'green');
|
||||
};
|
||||
|
|
|
@ -39,13 +39,13 @@ describe('getESUpgradeStatus', () => {
|
|||
// @ts-expect-error mock data is too loosely typed
|
||||
const deprecationsResponse: estypes.MigrationDeprecationsResponse = _.cloneDeep(fakeDeprecations);
|
||||
|
||||
const esClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
|
||||
|
||||
esClient.asCurrentUser.healthReport.mockResponse({ cluster_name: 'mock', indicators: {} });
|
||||
esClient.healthReport.mockResponse({ cluster_name: 'mock', indicators: {} });
|
||||
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse(deprecationsResponse);
|
||||
esClient.migration.deprecations.mockResponse(deprecationsResponse);
|
||||
|
||||
esClient.asCurrentUser.transport.request.mockResolvedValue({
|
||||
esClient.transport.request.mockResolvedValue({
|
||||
features: [
|
||||
{
|
||||
feature_name: 'machine_learning',
|
||||
|
@ -63,11 +63,11 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
// @ts-expect-error not full interface of response
|
||||
esClient.asCurrentUser.indices.resolveIndex.mockResponse(resolvedIndices);
|
||||
esClient.indices.resolveIndex.mockResponse(resolvedIndices);
|
||||
|
||||
it('calls /_migration/deprecations', async () => {
|
||||
await getESUpgradeStatus(esClient, { featureSet, dataSourceExclusions });
|
||||
expect(esClient.asCurrentUser.migration.deprecations).toHaveBeenCalled();
|
||||
expect(esClient.migration.deprecations).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns the correct shape of data', async () => {
|
||||
|
@ -76,7 +76,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
it('returns totalCriticalDeprecations > 0 when critical issues found', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
// @ts-expect-error not full interface
|
||||
cluster_settings: [{ level: 'critical', message: 'Do count me', url: 'https://...' }],
|
||||
node_settings: [],
|
||||
|
@ -93,7 +93,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
it('returns totalCriticalDeprecations === 0 when no critical issues found', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
// @ts-expect-error not full interface
|
||||
cluster_settings: [{ level: 'warning', message: 'Do not count me', url: 'https://...' }],
|
||||
node_settings: [],
|
||||
|
@ -110,7 +110,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
it('filters out system indices returned by upgrade system indices API', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
cluster_settings: [],
|
||||
node_settings: [],
|
||||
ml_settings: [],
|
||||
|
@ -149,7 +149,7 @@ describe('getESUpgradeStatus', () => {
|
|||
...esMigrationsMock.getMockMlSettingsDeprecations(),
|
||||
};
|
||||
// @ts-ignore missing property definitions in ES resolve_during_rolling_upgrade and _meta
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse(mockResponse);
|
||||
esClient.migration.deprecations.mockResponse(mockResponse);
|
||||
|
||||
const enabledUpgradeStatus = await getESUpgradeStatus(esClient, {
|
||||
featureSet,
|
||||
|
@ -181,7 +181,7 @@ describe('getESUpgradeStatus', () => {
|
|||
...esMigrationsMock.getMockEsDeprecations(),
|
||||
...esMigrationsMock.getMockDataStreamDeprecations(),
|
||||
} as MigrationDeprecationsResponse;
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse(mockResponse);
|
||||
esClient.migration.deprecations.mockResponse(mockResponse);
|
||||
|
||||
const enabledUpgradeStatus = await getESUpgradeStatus(esClient, {
|
||||
featureSet,
|
||||
|
@ -209,7 +209,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
it('filters out reindex corrective actions if featureSet.reindexCorrectiveActions is set to false', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
cluster_settings: [],
|
||||
node_settings: [
|
||||
{
|
||||
|
@ -253,7 +253,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
it('filters out old index deprecations enterprise search indices and data streams', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
cluster_settings: [],
|
||||
node_settings: [],
|
||||
ml_settings: [],
|
||||
|
@ -337,7 +337,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
});
|
||||
it('filters out frozen indices if old index deprecations exist for the same indices', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
cluster_settings: [],
|
||||
node_settings: [],
|
||||
ml_settings: [],
|
||||
|
@ -369,7 +369,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
// @ts-expect-error not full interface of response
|
||||
esClient.asCurrentUser.indices.resolveIndex.mockResponse(resolvedIndices);
|
||||
esClient.indices.resolveIndex.mockResponse(resolvedIndices);
|
||||
|
||||
const upgradeStatus = await getESUpgradeStatus(esClient, {
|
||||
featureSet,
|
||||
|
@ -384,7 +384,7 @@ describe('getESUpgradeStatus', () => {
|
|||
});
|
||||
|
||||
it('returns health indicators', async () => {
|
||||
esClient.asCurrentUser.migration.deprecations.mockResponse({
|
||||
esClient.migration.deprecations.mockResponse({
|
||||
cluster_settings: [],
|
||||
node_settings: [
|
||||
{
|
||||
|
@ -404,7 +404,7 @@ describe('getESUpgradeStatus', () => {
|
|||
templates: {},
|
||||
});
|
||||
|
||||
esClient.asCurrentUser.healthReport.mockResponse({
|
||||
esClient.healthReport.mockResponse({
|
||||
cluster_name: 'mock',
|
||||
indicators: {
|
||||
disk: healthIndicatorsMock.diskIndicatorGreen,
|
||||
|
@ -450,6 +450,11 @@ describe('getESUpgradeStatus', () => {
|
|||
Object {
|
||||
"correctiveAction": Object {
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "reindex",
|
||||
},
|
||||
"details": "This index was created using version: 6.8.13",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IScopedClusterClient } from '@kbn/core/server';
|
||||
import { ElasticsearchClient } from '@kbn/core/server';
|
||||
import {
|
||||
EnrichedDeprecationInfo,
|
||||
ESUpgradeStatus,
|
||||
|
@ -19,7 +19,7 @@ import { getHealthIndicators } from './health_indicators';
|
|||
import { matchExclusionPattern } from '../data_source_exclusions';
|
||||
|
||||
export async function getESUpgradeStatus(
|
||||
dataClient: IScopedClusterClient,
|
||||
dataClient: ElasticsearchClient,
|
||||
{
|
||||
featureSet,
|
||||
dataSourceExclusions,
|
||||
|
|
|
@ -9,9 +9,9 @@ import type {
|
|||
MigrationDeprecationsResponse,
|
||||
MigrationDeprecationsDeprecation,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
import { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
|
||||
import _ from 'lodash';
|
||||
import { EnrichedDeprecationInfo } from '../../../common/types';
|
||||
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import { omit } from 'lodash';
|
||||
import type { CorrectiveAction, EnrichedDeprecationInfo } from '../../../common/types';
|
||||
import {
|
||||
convertFeaturesToIndicesArray,
|
||||
getESSystemIndicesMigrationStatus,
|
||||
|
@ -32,10 +32,24 @@ interface EsDeprecations extends MigrationDeprecationsResponse {
|
|||
ilm_policies: Record<string, MigrationDeprecationsDeprecation[]>;
|
||||
}
|
||||
|
||||
const createBaseMigrationDeprecation = (
|
||||
export interface BaseDeprecation {
|
||||
index?: string;
|
||||
type: keyof EsDeprecations;
|
||||
details?: string;
|
||||
message: string;
|
||||
url: string;
|
||||
isCritical: boolean;
|
||||
metadata?: EsMetadata;
|
||||
resolveDuringUpgrade: boolean;
|
||||
// these properties apply to index_settings deprecations only
|
||||
isFrozenIndex?: boolean;
|
||||
isClosedIndex?: boolean;
|
||||
}
|
||||
|
||||
const createBaseDeprecation = (
|
||||
migrationDeprecation: MigrationDeprecationsDeprecation,
|
||||
{ deprecationType, indexName }: { deprecationType: keyof EsDeprecations; indexName?: string }
|
||||
) => {
|
||||
): BaseDeprecation => {
|
||||
const {
|
||||
details,
|
||||
message,
|
||||
|
@ -52,16 +66,16 @@ const createBaseMigrationDeprecation = (
|
|||
message,
|
||||
url,
|
||||
isCritical: level === 'critical',
|
||||
metadata,
|
||||
metadata: metadata as EsMetadata,
|
||||
resolveDuringUpgrade,
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeEsResponse = (migrationsResponse: EsDeprecations) => {
|
||||
const indexSettingsMigrations = Object.entries(migrationsResponse.index_settings).flatMap(
|
||||
const indexSettingsDeprecations = Object.entries(migrationsResponse.index_settings).flatMap(
|
||||
([indexName, migrationDeprecations]) => {
|
||||
return migrationDeprecations.flatMap((migrationDeprecation) =>
|
||||
createBaseMigrationDeprecation(migrationDeprecation, {
|
||||
createBaseDeprecation(migrationDeprecation, {
|
||||
indexName,
|
||||
deprecationType: 'index_settings',
|
||||
})
|
||||
|
@ -69,10 +83,10 @@ const normalizeEsResponse = (migrationsResponse: EsDeprecations) => {
|
|||
}
|
||||
);
|
||||
|
||||
const dataStreamsMigrations = Object.entries(migrationsResponse.data_streams).flatMap(
|
||||
const dataStreamsDeprecations = Object.entries(migrationsResponse.data_streams).flatMap(
|
||||
([indexName, dataStreamDeprecations]) => {
|
||||
return dataStreamDeprecations.flatMap((depractionData) =>
|
||||
createBaseMigrationDeprecation(depractionData, {
|
||||
createBaseDeprecation(depractionData, {
|
||||
indexName,
|
||||
deprecationType: 'data_streams',
|
||||
})
|
||||
|
@ -80,10 +94,10 @@ const normalizeEsResponse = (migrationsResponse: EsDeprecations) => {
|
|||
}
|
||||
);
|
||||
|
||||
const ilmPoliciesMigrations = Object.entries(migrationsResponse.ilm_policies).flatMap(
|
||||
const ilmPoliciesDeprecations = Object.entries(migrationsResponse.ilm_policies).flatMap(
|
||||
([indexName, ilmPolicyDeprecations]) => {
|
||||
return ilmPolicyDeprecations.flatMap((ilmPolicyData) =>
|
||||
createBaseMigrationDeprecation(ilmPolicyData, {
|
||||
createBaseDeprecation(ilmPolicyData, {
|
||||
indexName,
|
||||
deprecationType: 'ilm_policies',
|
||||
})
|
||||
|
@ -91,10 +105,10 @@ const normalizeEsResponse = (migrationsResponse: EsDeprecations) => {
|
|||
}
|
||||
);
|
||||
|
||||
const templatesMigrations = Object.entries(migrationsResponse.templates).flatMap(
|
||||
([indexName, templatesDeprecations]) => {
|
||||
return templatesDeprecations.flatMap((templatesDataa) =>
|
||||
createBaseMigrationDeprecation(templatesDataa, {
|
||||
const templatesDeprecations = Object.entries(migrationsResponse.templates).flatMap(
|
||||
([indexName, templateDeprecations]) => {
|
||||
return templateDeprecations.flatMap((templateData) =>
|
||||
createBaseDeprecation(templateData, {
|
||||
indexName,
|
||||
deprecationType: 'templates',
|
||||
})
|
||||
|
@ -102,129 +116,149 @@ const normalizeEsResponse = (migrationsResponse: EsDeprecations) => {
|
|||
}
|
||||
);
|
||||
|
||||
const mlSettingsMigrations = migrationsResponse.ml_settings.map((depractionData) =>
|
||||
createBaseMigrationDeprecation(depractionData, { deprecationType: 'ml_settings' })
|
||||
const mlSettingsDeprecations = migrationsResponse.ml_settings.map((depractionData) =>
|
||||
createBaseDeprecation(depractionData, { deprecationType: 'ml_settings' })
|
||||
);
|
||||
const nodeSettingsMigrations = migrationsResponse.node_settings.map((depractionData) =>
|
||||
createBaseMigrationDeprecation(depractionData, { deprecationType: 'node_settings' })
|
||||
const nodeSettingsDeprecations = migrationsResponse.node_settings.map((depractionData) =>
|
||||
createBaseDeprecation(depractionData, { deprecationType: 'node_settings' })
|
||||
);
|
||||
|
||||
const clusterSettingsMigrations = migrationsResponse.cluster_settings.map((depractionData) =>
|
||||
createBaseMigrationDeprecation(depractionData, { deprecationType: 'cluster_settings' })
|
||||
const clusterSettingsDeprecations = migrationsResponse.cluster_settings.map((depractionData) =>
|
||||
createBaseDeprecation(depractionData, { deprecationType: 'cluster_settings' })
|
||||
);
|
||||
|
||||
return [
|
||||
...clusterSettingsMigrations,
|
||||
...mlSettingsMigrations,
|
||||
...nodeSettingsMigrations,
|
||||
...indexSettingsMigrations,
|
||||
...dataStreamsMigrations,
|
||||
...ilmPoliciesMigrations,
|
||||
...templatesMigrations,
|
||||
...clusterSettingsDeprecations,
|
||||
...mlSettingsDeprecations,
|
||||
...nodeSettingsDeprecations,
|
||||
...indexSettingsDeprecations,
|
||||
...dataStreamsDeprecations,
|
||||
...ilmPoliciesDeprecations,
|
||||
...templatesDeprecations,
|
||||
].flat();
|
||||
};
|
||||
|
||||
export const getEnrichedDeprecations = async (
|
||||
dataClient: IScopedClusterClient
|
||||
): Promise<EnrichedDeprecationInfo[]> => {
|
||||
const deprecations = (await dataClient.asCurrentUser.migration.deprecations()) as EsDeprecations;
|
||||
const systemIndices = await getESSystemIndicesMigrationStatus(dataClient.asCurrentUser);
|
||||
|
||||
const systemIndicesList = convertFeaturesToIndicesArray(systemIndices.features);
|
||||
|
||||
const indexSettingsIndexNames = Object.keys(deprecations.index_settings);
|
||||
const indexSettingsIndexStates = indexSettingsIndexNames.length
|
||||
? await esIndicesStateCheck(dataClient.asCurrentUser, indexSettingsIndexNames)
|
||||
: {};
|
||||
|
||||
const deprecationsByIndex = new Map<string, EnrichedDeprecationInfo[]>();
|
||||
|
||||
return normalizeEsResponse(deprecations)
|
||||
.filter((deprecation) => {
|
||||
switch (deprecation.type) {
|
||||
case 'index_settings': {
|
||||
if (!deprecation.index) {
|
||||
return false;
|
||||
}
|
||||
// filter out system indices
|
||||
return !systemIndicesList.includes(deprecation.index);
|
||||
}
|
||||
case 'cluster_settings':
|
||||
case 'templates':
|
||||
case 'ilm_policies':
|
||||
case 'ml_settings':
|
||||
case 'node_settings':
|
||||
case 'data_streams': {
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
// Throwing here to avoid allowing upgrades while we have unhandled deprecation types from ES
|
||||
// That might cause the stack to fail to start after upgrade.
|
||||
throw new Error(`Unknown ES deprecation type "${deprecation.type}"`);
|
||||
}
|
||||
}
|
||||
})
|
||||
.flatMap((deprecation) => {
|
||||
const correctiveAction = getCorrectiveAction(
|
||||
deprecation.type,
|
||||
deprecation.message,
|
||||
deprecation.metadata as EsMetadata,
|
||||
deprecation.index
|
||||
);
|
||||
|
||||
// Early exclusion of deprecations
|
||||
if (
|
||||
(deprecation.type === 'index_settings' &&
|
||||
correctiveAction?.type === 'reindex' &&
|
||||
deprecation.index?.startsWith(ENT_SEARCH_INDEX_PREFIX)) ||
|
||||
(deprecation.type === 'data_streams' &&
|
||||
correctiveAction?.type === 'dataStream' &&
|
||||
correctiveAction.metadata.reindexRequired &&
|
||||
ENT_SEARCH_DATASTREAM_PREFIXES.some((prefix) => deprecation.index?.startsWith(prefix)))
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If we have found deprecation information for index/indices
|
||||
// check whether the index is open or closed.
|
||||
if (deprecation.type === 'index_settings' && correctiveAction?.type === 'reindex') {
|
||||
correctiveAction.blockerForReindexing =
|
||||
indexSettingsIndexStates[deprecation.index!] === 'closed' ? 'index-closed' : undefined;
|
||||
}
|
||||
|
||||
const enrichedDeprecation = {
|
||||
..._.omit(deprecation, 'metadata'),
|
||||
correctiveAction,
|
||||
};
|
||||
|
||||
if (deprecation.index) {
|
||||
const indexDeprecations = deprecationsByIndex.get(deprecation.index) || [];
|
||||
indexDeprecations.push(enrichedDeprecation);
|
||||
deprecationsByIndex.set(deprecation.index, indexDeprecations);
|
||||
}
|
||||
|
||||
return enrichedDeprecation;
|
||||
})
|
||||
.filter((deprecation) => {
|
||||
if (isFrozenDeprecation(deprecation.message, deprecation.index)) {
|
||||
// frozen indices are created in 7.x, so they are old / incompatible as well
|
||||
// no need to bubble up this deprecation IF THERE IS ANOTHER CRITICAL ONE FOR THE SAME INDEX
|
||||
// in that case, in the critical deprecation we will propose:
|
||||
// - reindexing => the new index will not be frozen
|
||||
// - updating index => the operation will unfreeze the index (see routes/update_index.ts)
|
||||
const indexDeprecations = deprecationsByIndex.get(deprecation.index!);
|
||||
const oldIndexDeprecation: EnrichedDeprecationInfo | undefined = indexDeprecations?.find(
|
||||
(elem) =>
|
||||
elem.type === 'index_settings' &&
|
||||
elem.index === deprecation.index &&
|
||||
elem.correctiveAction?.type === 'reindex' &&
|
||||
elem.isCritical
|
||||
);
|
||||
if (oldIndexDeprecation) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const isKnownDeprecation = (deprecation: BaseDeprecation): boolean => {
|
||||
switch (deprecation.type) {
|
||||
case 'index_settings':
|
||||
case 'cluster_settings':
|
||||
case 'templates':
|
||||
case 'ilm_policies':
|
||||
case 'ml_settings':
|
||||
case 'node_settings':
|
||||
case 'data_streams': {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const enrichIndexSettingsDeprecations = async (
|
||||
esClient: ElasticsearchClient,
|
||||
deprecations: BaseDeprecation[]
|
||||
): Promise<void> => {
|
||||
const deprecationsByIndex = new Map<string, BaseDeprecation[]>();
|
||||
const indexSettingsDeprecations = deprecations.filter(
|
||||
(deprecation) => deprecation.type === 'index_settings'
|
||||
);
|
||||
|
||||
// we do a first pass to store all the index deprecations in a Map
|
||||
indexSettingsDeprecations.forEach((deprecation) => {
|
||||
const indexDeprecations = deprecationsByIndex.get(deprecation.index!) ?? [];
|
||||
indexDeprecations.push(deprecation);
|
||||
deprecationsByIndex.set(deprecation.index!, indexDeprecations);
|
||||
});
|
||||
|
||||
// fetch open/closed state for all of the index_settings deprecations indices
|
||||
const indexNames = Array.from(deprecationsByIndex.keys());
|
||||
const indexStates = indexNames.length ? await esIndicesStateCheck(esClient, indexNames) : {};
|
||||
|
||||
// Update some properties for each of the index_settings deprecations
|
||||
indexSettingsDeprecations.forEach((deprecation) => {
|
||||
deprecation.isClosedIndex = indexStates[deprecation.index!] === 'closed';
|
||||
|
||||
// check if a given deprecation is a "frozen index deprecation"
|
||||
const isFrozenIndex = isFrozenDeprecation(deprecation.message, deprecation.index);
|
||||
|
||||
// update all deprecations for the same index
|
||||
if (isFrozenIndex) {
|
||||
deprecationsByIndex
|
||||
.get(deprecation.index!)!
|
||||
.forEach((indexDeprecation) => (indexDeprecation.isFrozenIndex = true));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const excludeDeprecation = (
|
||||
deprecation: BaseDeprecation,
|
||||
correctiveAction?: CorrectiveAction
|
||||
): boolean => {
|
||||
if (
|
||||
deprecation.type === 'index_settings' &&
|
||||
correctiveAction?.type === 'reindex' &&
|
||||
deprecation.index?.startsWith(ENT_SEARCH_INDEX_PREFIX)
|
||||
) {
|
||||
return true;
|
||||
} else if (
|
||||
deprecation.type === 'data_streams' &&
|
||||
correctiveAction?.type === 'dataStream' &&
|
||||
correctiveAction.metadata.reindexRequired &&
|
||||
ENT_SEARCH_DATASTREAM_PREFIXES.some((prefix) => deprecation.index?.startsWith(prefix))
|
||||
) {
|
||||
return true;
|
||||
} else if (
|
||||
deprecation.isCritical &&
|
||||
deprecation.type === 'index_settings' &&
|
||||
deprecation.isFrozenIndex &&
|
||||
correctiveAction?.type === 'reindex'
|
||||
) {
|
||||
// in this scenario we will already have a "frozen index" deprecation for the same index
|
||||
// we will filter this 'reindex' deprecation out, and let the 'unfreeze' one pass through
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const getEnrichedDeprecations = async (
|
||||
esClient: ElasticsearchClient
|
||||
): Promise<EnrichedDeprecationInfo[]> => {
|
||||
const esDeprecations = (await esClient.migration.deprecations()) as EsDeprecations;
|
||||
const deprecations = normalizeEsResponse(esDeprecations);
|
||||
|
||||
// Throwing here to avoid allowing upgrades while we have unhandled deprecation types from ES
|
||||
// That might cause the stack to fail to start after upgrade.
|
||||
deprecations.forEach((deprecation) => {
|
||||
if (!isKnownDeprecation(deprecation)) {
|
||||
throw new Error(`Unknown ES deprecation type "${deprecation.type}"`);
|
||||
}
|
||||
});
|
||||
|
||||
// Kibana system indices are handled in a different section of the Upgrade Assistant
|
||||
const systemIndices = await getESSystemIndicesMigrationStatus(esClient);
|
||||
const systemIndicesList = convertFeaturesToIndicesArray(systemIndices.features);
|
||||
const filteredDeprecations = deprecations.filter(
|
||||
(deprecation) =>
|
||||
deprecation.type !== 'index_settings' || !systemIndicesList.includes(deprecation.index!)
|
||||
);
|
||||
|
||||
// Set extra metadata properties for index_settings deprecations
|
||||
await enrichIndexSettingsDeprecations(esClient, filteredDeprecations);
|
||||
|
||||
// enrich deprecations with the corrective actions, remove metadata
|
||||
return filteredDeprecations.flatMap((deprecation) => {
|
||||
const correctiveAction = getCorrectiveAction(deprecation);
|
||||
|
||||
// Prevent some deprecations from showing up in the UI
|
||||
if (excludeDeprecation(deprecation, correctiveAction)) {
|
||||
return []; // equivalent of filtering out, thanks to the flatMap
|
||||
}
|
||||
|
||||
return {
|
||||
...omit(deprecation, 'metadata', 'isFrozenIndex', 'isClosedIndex', 'isInDataStream'),
|
||||
correctiveAction,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
@ -12,19 +12,16 @@ import { ResolveIndexResponseFromES } from '../../common/types';
|
|||
type StatusCheckResult = Record<string, 'open' | 'closed'>;
|
||||
|
||||
export const esIndicesStateCheck = async (
|
||||
asCurrentUser: ElasticsearchClient,
|
||||
esClient: ElasticsearchClient,
|
||||
indices: string[]
|
||||
): Promise<StatusCheckResult> => {
|
||||
const response = await asCurrentUser.indices.resolveIndex({
|
||||
const response = await esClient.indices.resolveIndex({
|
||||
name: '*',
|
||||
expand_wildcards: 'all',
|
||||
});
|
||||
|
||||
const result: StatusCheckResult = {};
|
||||
|
||||
indices.forEach((index) => {
|
||||
result[index] = getIndexState(index, response as ResolveIndexResponseFromES);
|
||||
});
|
||||
|
||||
return result;
|
||||
return indices.reduce<StatusCheckResult>((acc, index) => {
|
||||
acc[index] = getIndexState(index, response as ResolveIndexResponseFromES);
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
|
|
@ -116,9 +116,11 @@ export interface ReindexService {
|
|||
* Obtain metadata about the index, including aliases and settings
|
||||
* @param indexName
|
||||
*/
|
||||
getIndexInfo(
|
||||
indexName: string
|
||||
): Promise<{ aliases: Record<string, IndicesAlias>; settings?: IndicesIndexSettings }>;
|
||||
getIndexInfo(indexName: string): Promise<{
|
||||
aliases: Record<string, IndicesAlias>;
|
||||
settings?: IndicesIndexSettings;
|
||||
isInDataStream: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const reindexServiceFactory = (
|
||||
|
@ -356,7 +358,8 @@ export const reindexServiceFactory = (
|
|||
|
||||
const aliases = response[indexName]?.aliases ?? {};
|
||||
const settings = response[indexName]?.settings?.index ?? {};
|
||||
return { aliases, settings };
|
||||
const isInDataStream = Boolean(response[indexName]?.data_stream);
|
||||
return { aliases, settings, isInDataStream };
|
||||
};
|
||||
|
||||
const isIndexHidden = async (indexName: string) => {
|
||||
|
|
|
@ -43,6 +43,7 @@ describe('ES deprecations API', () => {
|
|||
},
|
||||
router: mockRouter,
|
||||
lib: { handleEsError },
|
||||
log: { error: jest.fn() },
|
||||
};
|
||||
registerESDeprecationRoutes(routeDependencies);
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { API_BASE_PATH } from '../../common/constants';
|
||||
import { getESUpgradeStatus } from '../lib/es_deprecations_status';
|
||||
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
|
||||
import { RouteDependencies } from '../types';
|
||||
import type { RouteDependencies } from '../types';
|
||||
import { reindexActionsFactory } from '../lib/reindexing/reindex_actions';
|
||||
import { reindexServiceFactory } from '../lib/reindexing';
|
||||
|
||||
|
@ -36,8 +36,10 @@ export function registerESDeprecationRoutes({
|
|||
savedObjects: { client: savedObjectsClient },
|
||||
elasticsearch: { client },
|
||||
} = await core;
|
||||
const status = await getESUpgradeStatus(client, { featureSet, dataSourceExclusions });
|
||||
|
||||
const status = await getESUpgradeStatus(client.asCurrentUser, {
|
||||
featureSet,
|
||||
dataSourceExclusions,
|
||||
});
|
||||
const asCurrentUser = client.asCurrentUser;
|
||||
const reindexActions = reindexActionsFactory(savedObjectsClient, asCurrentUser);
|
||||
const reindexService = reindexServiceFactory(asCurrentUser, reindexActions, log, licensing);
|
||||
|
@ -51,6 +53,7 @@ export function registerESDeprecationRoutes({
|
|||
body: status,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
})
|
||||
|
|
|
@ -126,7 +126,7 @@ export function registerReindexIndicesRoutes(
|
|||
: [];
|
||||
|
||||
const isTruthy = (value?: string | boolean): boolean => value === true || value === 'true';
|
||||
const { aliases, settings } = await reindexService.getIndexInfo(indexName);
|
||||
const { aliases, settings, isInDataStream } = await reindexService.getIndexInfo(indexName);
|
||||
|
||||
const body: ReindexStatusResponse = {
|
||||
reindexOp: reindexOp ? reindexOp.attributes : undefined,
|
||||
|
@ -138,6 +138,7 @@ export function registerReindexIndicesRoutes(
|
|||
aliases: Object.keys(aliases),
|
||||
isFrozen: isTruthy(settings?.frozen),
|
||||
isReadonly: isTruthy(settings?.verified_read_only),
|
||||
isInDataStream,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ describe('Status API', () => {
|
|||
lib: { handleEsError },
|
||||
current: currentVersion,
|
||||
defaultTarget: nextMajor,
|
||||
log: { error: jest.fn() },
|
||||
};
|
||||
|
||||
registerUpgradeStatusRoute(routeDependencies);
|
||||
|
@ -278,6 +279,7 @@ describe('Status API', () => {
|
|||
lib: { handleEsError },
|
||||
current: currentVersion,
|
||||
defaultTarget: nextMajor,
|
||||
log: { error: jest.fn() },
|
||||
};
|
||||
|
||||
registerUpgradeStatusRoute(routeDependencies);
|
||||
|
|
|
@ -23,6 +23,7 @@ export function registerUpgradeStatusRoute({
|
|||
lib: { handleEsError },
|
||||
current,
|
||||
defaultTarget,
|
||||
log,
|
||||
}: RouteDependencies) {
|
||||
router.get(
|
||||
{
|
||||
|
@ -57,7 +58,7 @@ export function registerUpgradeStatusRoute({
|
|||
const {
|
||||
totalCriticalDeprecations, // critical deprecations
|
||||
totalCriticalHealthIssues, // critical health issues
|
||||
} = await getESUpgradeStatus(esClient, { featureSet, dataSourceExclusions });
|
||||
} = await getESUpgradeStatus(esClient.asCurrentUser, { featureSet, dataSourceExclusions });
|
||||
|
||||
const getSystemIndicesMigrationStatus = async () => {
|
||||
/**
|
||||
|
@ -159,6 +160,7 @@ export function registerUpgradeStatusRoute({
|
|||
},
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
return handleEsError({ error, response });
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue