mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Core][UA] Forward-port 8.18 changes into main (#214996)
## Summary We've done a lot of UA changes against 8.18 and 8.x but we did not port them to `main`. The reason we did not do the active development against `main` is because UA is disabled on main and we need those features in 8.last not on `9.0` initially. This port is to keep the codebase consistent and to be able to use these new UA features in the future post `9.0`. Any issues that that are caused from this port we should wrap them in a config and disabled them on main and enable this new flag on `8.x`. What is being ported? - [x] Upgrade assistant plugin (`x-pack/platform/plugins/private/upgrade_assistant`) - [x] Integration test changes (`x-pack/test/upgrade_assistant_integration`) - [x] new UA doc links (`src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts`) - [x] localization files sync (`x-pack/platform/plugins/private/translations/translations/*.json`) - [x] Unfreeze is no longer supported after `8.x`. So removed it from data streams readonly migration step and throw an error for regular indices migration. Closes https://github.com/elastic/kibana/issues/210490 --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
11a512e735
commit
f83612f4e0
132 changed files with 6303 additions and 1859 deletions
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -883,7 +883,7 @@ x-pack/platform/plugins/private/snapshot_restore @elastic/kibana-management
|
|||
x-pack/platform/plugins/private/telemetry_collection_xpack @elastic/kibana-core
|
||||
x-pack/platform/plugins/private/transform @elastic/ml-ui
|
||||
x-pack/platform/plugins/private/translations @elastic/kibana-localization
|
||||
x-pack/platform/plugins/private/upgrade_assistant @elastic/kibana-core
|
||||
x-pack/platform/plugins/private/upgrade_assistant @elastic/kibana-management
|
||||
x-pack/platform/plugins/private/watcher @elastic/kibana-management
|
||||
x-pack/platform/plugins/shared/actions @elastic/response-ops
|
||||
x-pack/platform/plugins/shared/ai_infra/llm_tasks @elastic/appex-ai-infra
|
||||
|
|
|
@ -200,6 +200,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
|
|||
trainedModels: `${MACHINE_LEARNING_DOCS}ml-trained-models.html`,
|
||||
textEmbedding: `${MACHINE_LEARNING_DOCS}ml-nlp-model-ref.html#ml-nlp-model-ref-text-embedding`,
|
||||
troubleshootSetup: `${ENTERPRISE_SEARCH_DOCS}troubleshoot-setup.html`,
|
||||
upgrade9x: `${ENTERPRISE_SEARCH_DOCS}upgrading-to-9-x.html`,
|
||||
usersAccess: `${ENTERPRISE_SEARCH_DOCS}users-access.html`,
|
||||
},
|
||||
metricbeat: {
|
||||
|
@ -301,6 +302,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
|
|||
upgradeAssistant: {
|
||||
overview: `${KIBANA_DOCS}upgrade-assistant.html`,
|
||||
batchReindex: `${KIBANA_DOCS}batch-start-resume-reindex.html`,
|
||||
indexBlocks: `${ELASTICSEARCH_DOCS}index-modules-blocks.html#index-block-settings`,
|
||||
remoteReindex: `${ELASTICSEARCH_DOCS}docs-reindex.html#reindex-from-remote`,
|
||||
unfreezeApi: `${ELASTICSEARCH_DOCS}unfreeze-index-api.html`,
|
||||
reindexWithPipeline: `${ELASTICSEARCH_DOCS}docs-reindex.html#reindex-with-an-ingest-pipeline`,
|
||||
|
|
|
@ -169,6 +169,7 @@ export interface DocLinks {
|
|||
readonly textEmbedding: string;
|
||||
readonly troubleshootSetup: string;
|
||||
readonly usersAccess: string;
|
||||
readonly upgrade9x: string;
|
||||
};
|
||||
readonly heartbeat: {
|
||||
readonly base: string;
|
||||
|
@ -261,6 +262,7 @@ export interface DocLinks {
|
|||
readonly upgradeAssistant: {
|
||||
readonly overview: string;
|
||||
readonly batchReindex: string;
|
||||
readonly indexBlocks: string;
|
||||
readonly remoteReindex: string;
|
||||
readonly unfreezeApi: string;
|
||||
readonly reindexWithPipeline: string;
|
||||
|
|
|
@ -47450,9 +47450,6 @@
|
|||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.deletingButtonLabel": "Retrait des paramètres en cours…",
|
||||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionText": "Retirer les paramètres",
|
||||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionTooltipLabel": "Corrigez ce problème en retirant les paramètres de ce cluster. La correction peut s'effectuer automatiquement.",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.reindexLoadingStatusText": "Chargement du statut…",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.resolutionLabel": "Marquer en lecture seule ou réindexer",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipLabel": "Ce problème peut être résolu en réindexant ce flux de données ou en marquant ses index en lecture seule. La correction peut s'effectuer automatiquement.",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStreamsTypeLabel": "Flux de données",
|
||||
"xpack.upgradeAssistant.esDeprecations.defaultDeprecation.manualCellLabel": "Manuel",
|
||||
"xpack.upgradeAssistant.esDeprecations.deprecationDetailsFlyout.closeButtonLabel": "Fermer",
|
||||
|
|
|
@ -47415,9 +47415,6 @@
|
|||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.deletingButtonLabel": "設定を削除中…",
|
||||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionText": "設定の削除",
|
||||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionTooltipLabel": "このクラスターから設定を削除して、この問題を解決します。この問題は自動的に解決できます。",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.reindexLoadingStatusText": "ステータスを読み込んでいます...",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.resolutionLabel": "読み取り専用に設定または再インデックス",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipLabel": "この問題を解決するには、このデータストリームを再インデックス化するか、そのインデックスを読み取り専用に設定します。この問題は自動的に解決できます。",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStreamsTypeLabel": "データストリーム",
|
||||
"xpack.upgradeAssistant.esDeprecations.defaultDeprecation.manualCellLabel": "手動",
|
||||
"xpack.upgradeAssistant.esDeprecations.deprecationDetailsFlyout.closeButtonLabel": "閉じる",
|
||||
|
|
|
@ -47489,9 +47489,6 @@
|
|||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.deletingButtonLabel": "设置移除进行中……",
|
||||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionText": "移除设置",
|
||||
"xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionTooltipLabel": "通过从此集群中移除设置来解决该问题。此问题会自动解决。",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.reindexLoadingStatusText": "正在加载状态……",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.resolutionLabel": "标记为只读,或重新索引",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipLabel": "请通过重新索引该数据流或将其索引标记为只读来解决此问题。此问题会自动解决。",
|
||||
"xpack.upgradeAssistant.esDeprecations.dataStreamsTypeLabel": "数据流",
|
||||
"xpack.upgradeAssistant.esDeprecations.defaultDeprecation.manualCellLabel": "手动",
|
||||
"xpack.upgradeAssistant.esDeprecations.deprecationDetailsFlyout.closeButtonLabel": "关闭",
|
||||
|
|
|
@ -29,7 +29,7 @@ These surface runtime deprecations, e.g. a Painless script that uses a deprecate
|
|||
request to a deprecated API. These are also generally surfaced as deprecation headers within the
|
||||
response. Even if the cluster state is good, app maintainers need to watch the logs in case
|
||||
deprecations are discovered as data is migrated. Starting in 7.x, deprecation logs can be written to a file or a data stream ([#58924](https://github.com/elastic/elasticsearch/pull/58924)). When the data stream exists, the Upgrade Assistant provides a way to analyze the logs through Observability or Discover ([#106521](https://github.com/elastic/kibana/pull/106521)).
|
||||
* [**Kibana deprecations API.**](https://github.com/elastic/kibana/blob/main/src/core/server/docs/kib_core_deprecations_service.mdx) This is information about deprecated features and configs in Kibana. These deprecations are only communicated to the user if the deployment is using these features. Kibana engineers are responsible for adding deprecations to the deprecations API for their respective team.
|
||||
* [**Kibana deprecations API.**](https://github.com/elastic/kibana/blob/main/src/core/server/deprecations/README.mdx) This is information about deprecated features and configs in Kibana. These deprecations are only communicated to the user if the deployment is using these features. Kibana engineers are responsible for adding deprecations to the deprecations API for their respective team.
|
||||
|
||||
### Fixing problems
|
||||
|
||||
|
|
|
@ -35,6 +35,11 @@ export const MOCK_REINDEX_DEPRECATION: EnrichedDeprecationInfo = {
|
|||
index: 'reindex_index',
|
||||
correctiveAction: {
|
||||
type: 'reindex',
|
||||
metadata: {
|
||||
isClosedIndex: false,
|
||||
isFrozenIndex: false,
|
||||
isInDataStream: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ const defaultReindexStatusMeta: ReindexStatusResponse['meta'] = {
|
|||
indexName: 'foo',
|
||||
reindexName: 'reindexed-foo',
|
||||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
};
|
||||
|
||||
describe('Reindex deprecation flyout', () => {
|
||||
|
@ -75,28 +78,22 @@ describe('Reindex deprecation flyout', () => {
|
|||
|
||||
expect(exists('reindexDetails')).toBe(true);
|
||||
expect(find('reindexDetails.flyoutTitle').text()).toContain(
|
||||
`Reindex ${reindexDeprecation.index}`
|
||||
`Update ${reindexDeprecation.index}`
|
||||
);
|
||||
});
|
||||
|
||||
it('renders error callout when reindex fails', async () => {
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
|
||||
const { actions, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
|
||||
httpRequestsMockHelpers.setStartReindexingResponse(MOCK_REINDEX_DEPRECATION.index!, undefined, {
|
||||
statusCode: 404,
|
||||
message: 'no such index [test]',
|
||||
});
|
||||
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton();
|
||||
const { actions, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
expect(exists('reindexDetails')).toBe(true);
|
||||
expect(exists('reindexDetails.reindexingFailedCallout')).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -106,28 +103,16 @@ describe('Reindex deprecation flyout', () => {
|
|||
message: 'no such index [test]',
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
|
||||
const { actions, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
|
||||
expect(exists('reindexDetails.fetchFailedCallout')).toBe(true);
|
||||
});
|
||||
|
||||
describe('reindexing progress', () => {
|
||||
it('has not started yet', async () => {
|
||||
const { actions, find, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
expect(find('reindexChecklistTitle').text()).toEqual('Reindexing process');
|
||||
expect(exists('cancelReindexingDocumentsButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('has started but not yet reindexing documents', async () => {
|
||||
httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, {
|
||||
reindexOp: {
|
||||
|
@ -140,14 +125,11 @@ describe('Reindex deprecation flyout', () => {
|
|||
meta: defaultReindexStatusMeta,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
const { actions, find, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
|
||||
expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 5%');
|
||||
expect(exists('cancelReindexingDocumentsButton')).toBe(false);
|
||||
|
@ -165,14 +147,11 @@ describe('Reindex deprecation flyout', () => {
|
|||
meta: defaultReindexStatusMeta,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
const { actions, find, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
|
||||
expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 30%');
|
||||
expect(exists('cancelReindexingDocumentsButton')).toBe(true);
|
||||
|
@ -190,14 +169,11 @@ describe('Reindex deprecation flyout', () => {
|
|||
meta: defaultReindexStatusMeta,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
const { actions, find, exists } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
|
||||
expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 90%');
|
||||
expect(exists('cancelReindexingDocumentsButton')).toBe(false);
|
||||
|
@ -215,14 +191,11 @@ describe('Reindex deprecation flyout', () => {
|
|||
meta: defaultReindexStatusMeta,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
const { actions, find, exists, component } = testBed;
|
||||
component.update();
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
|
||||
expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 95%');
|
||||
expect(exists('cancelReindexingDocumentsButton')).toBe(false);
|
||||
|
@ -250,14 +223,11 @@ describe('Reindex deprecation flyout', () => {
|
|||
},
|
||||
]);
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupElasticsearchPage(httpSetup);
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
const { actions, find } = testBed;
|
||||
|
||||
await actions.table.clickDeprecationRowAt('reindex', 0);
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // details step
|
||||
await actions.reindexDeprecationFlyout.clickReindexButton(); // warning step
|
||||
|
||||
expect(find('lowDiskSpaceCallout').text()).toContain('Nodes with low disk space');
|
||||
expect(find('impactedNodeListItem').length).toEqual(1);
|
||||
|
|
|
@ -51,6 +51,7 @@ const servicesMock = {
|
|||
const idToUrlMap = {
|
||||
SNAPSHOT_RESTORE_LOCATOR: 'snapshotAndRestoreUrl',
|
||||
DISCOVER_APP_LOCATOR: 'discoverUrl',
|
||||
OBS_LOGS_EXPLORER_DATA_VIEW_LOCATOR: 'logsExplorerUrl',
|
||||
};
|
||||
type IdKey = keyof typeof idToUrlMap;
|
||||
|
||||
|
@ -75,6 +76,7 @@ shareMock.url.locators.get = (id: IdKey) => ({
|
|||
});
|
||||
|
||||
export const getAppContextMock = (kibanaVersion: SemVer) => ({
|
||||
dataSourceExclusions: {},
|
||||
featureSet: {
|
||||
mlSnapshots: true,
|
||||
migrateSystemIndices: true,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
export interface DataStreamsActionMetadata {
|
||||
excludedActions?: Array<'readOnly' | 'reindex'>;
|
||||
totalBackingIndices: number;
|
||||
indicesRequiringUpgradeCount: number;
|
||||
indicesRequiringUpgrade: string[];
|
||||
|
@ -14,6 +15,7 @@ export interface DataStreamsActionMetadata {
|
|||
reindexRequired: boolean;
|
||||
}
|
||||
|
||||
export type DataStreamResolutionType = 'readonly' | 'reindex';
|
||||
export interface DataStreamsAction {
|
||||
type: 'dataStream';
|
||||
metadata: DataStreamsActionMetadata;
|
||||
|
@ -34,21 +36,22 @@ export interface DataStreamMetadata {
|
|||
}
|
||||
|
||||
export interface DataStreamReindexStatusResponse {
|
||||
warnings?: DataStreamReindexWarning[];
|
||||
reindexOp?: DataStreamReindexOperation;
|
||||
warnings?: DataStreamMigrationWarning[];
|
||||
migrationOp?: DataStreamMigrationOperation;
|
||||
hasRequiredPrivileges?: boolean;
|
||||
}
|
||||
|
||||
export type DataStreamReindexWarningTypes = 'incompatibleDataStream';
|
||||
export type DataStreamWarningTypes = 'incompatibleDataStream' | 'affectExistingSetups';
|
||||
|
||||
export interface DataStreamReindexWarning {
|
||||
warningType: DataStreamReindexWarningTypes;
|
||||
export interface DataStreamMigrationWarning {
|
||||
warningType: DataStreamWarningTypes;
|
||||
resolutionType: DataStreamResolutionType;
|
||||
meta?: {
|
||||
[key: string]: string | string[];
|
||||
};
|
||||
}
|
||||
|
||||
export enum DataStreamReindexStatus {
|
||||
export enum DataStreamMigrationStatus {
|
||||
notStarted,
|
||||
inProgress,
|
||||
completed,
|
||||
|
@ -66,31 +69,35 @@ export interface DataStreamProgressDetails {
|
|||
}
|
||||
|
||||
export interface DataStreamReindexStatusNotStarted {
|
||||
status: DataStreamReindexStatus.notStarted;
|
||||
status: DataStreamMigrationStatus.notStarted;
|
||||
}
|
||||
|
||||
export interface DataStreamReindexStatusInProgress {
|
||||
status: DataStreamReindexStatus.inProgress;
|
||||
reindexTaskPercComplete: number;
|
||||
resolutionType: 'reindex' | 'readonly';
|
||||
status: DataStreamMigrationStatus.inProgress;
|
||||
taskPercComplete: number;
|
||||
progressDetails: DataStreamProgressDetails;
|
||||
}
|
||||
|
||||
export interface DataStreamReindexStatusCompleted {
|
||||
status: DataStreamReindexStatus.completed;
|
||||
reindexTaskPercComplete: number;
|
||||
resolutionType: 'reindex' | 'readonly';
|
||||
status: DataStreamMigrationStatus.completed;
|
||||
taskPercComplete: number;
|
||||
progressDetails: DataStreamProgressDetails;
|
||||
}
|
||||
|
||||
export interface DataStreamReindexStatusFailed {
|
||||
status: DataStreamReindexStatus.failed;
|
||||
resolutionType: 'reindex' | 'readonly';
|
||||
status: DataStreamMigrationStatus.failed;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export interface DataStreamReindexStatusCancelled {
|
||||
status: DataStreamReindexStatus.cancelled;
|
||||
resolutionType: 'reindex' | 'readonly';
|
||||
status: DataStreamMigrationStatus.cancelled;
|
||||
}
|
||||
|
||||
export type DataStreamReindexOperation =
|
||||
export type DataStreamMigrationOperation =
|
||||
| DataStreamReindexStatusNotStarted
|
||||
| DataStreamReindexStatusInProgress
|
||||
| DataStreamReindexStatusCompleted
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { HealthReportImpact } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/types';
|
||||
import { SavedObject } from '@kbn/core/types';
|
||||
import type { DataStreamsAction } from './data_stream_types';
|
||||
|
||||
|
@ -54,8 +54,11 @@ export interface ReindexStatusResponse {
|
|||
reindexName: string;
|
||||
// Array of aliases pointing to the index being reindexed
|
||||
aliases: string[];
|
||||
isReadonly: boolean;
|
||||
isFrozen: boolean;
|
||||
isInDataStream: boolean;
|
||||
};
|
||||
warnings?: ReindexWarning[];
|
||||
warnings?: IndexWarning[];
|
||||
reindexOp?: ReindexOperation;
|
||||
hasRequiredPrivileges?: boolean;
|
||||
}
|
||||
|
@ -136,10 +139,11 @@ export interface ReindexOperation {
|
|||
export type ReindexSavedObject = SavedObject<ReindexOperation>;
|
||||
|
||||
// 8.0 -> 9.0 warnings
|
||||
export type ReindexWarningTypes = 'indexSetting' | 'replaceIndexWithAlias';
|
||||
export type IndexWarningType = 'indexSetting' | 'replaceIndexWithAlias' | 'makeIndexReadonly';
|
||||
|
||||
export interface ReindexWarning {
|
||||
warningType: ReindexWarningTypes;
|
||||
export interface IndexWarning {
|
||||
warningType: IndexWarningType;
|
||||
flow: 'reindex' | 'readonly' | 'all';
|
||||
/**
|
||||
* Optional metadata for deprecations
|
||||
*
|
||||
|
@ -147,7 +151,7 @@ export interface ReindexWarning {
|
|||
* For "indexSetting" we want to surface the deprecated settings.
|
||||
*/
|
||||
meta?: {
|
||||
[key: string]: string | string[];
|
||||
[key: string]: string | string[] | boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -190,16 +194,36 @@ 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
|
||||
*/
|
||||
transformIds?: string[];
|
||||
|
||||
/**
|
||||
* The actions that should be excluded from the reindex corrective action.
|
||||
*/
|
||||
excludedActions?: string[];
|
||||
}
|
||||
|
||||
export interface UnfreezeAction extends IndexAction {
|
||||
type: 'unfreeze';
|
||||
}
|
||||
|
||||
export interface MlAction {
|
||||
|
@ -225,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,
|
||||
|
@ -237,16 +270,9 @@ export interface EnrichedDeprecationInfo
|
|||
| 'ilm_policies'
|
||||
| 'templates';
|
||||
isCritical: boolean;
|
||||
frozen?: boolean;
|
||||
status?: estypes.HealthReportIndicatorHealthStatus;
|
||||
index?: string;
|
||||
correctiveAction?:
|
||||
| ReindexAction
|
||||
| MlAction
|
||||
| IndexSettingAction
|
||||
| ClusterSettingAction
|
||||
| DataStreamsAction
|
||||
| HealthIndicatorAction;
|
||||
correctiveAction?: CorrectiveAction;
|
||||
resolveDuringUpgrade: boolean;
|
||||
}
|
||||
|
||||
|
@ -322,3 +348,5 @@ export interface FeatureSet {
|
|||
reindexCorrectiveActions: boolean;
|
||||
migrateDataStreams: boolean;
|
||||
}
|
||||
|
||||
export type DataSourceExclusions = Record<string, Array<'readOnly' | 'reindex'>>;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type UpdateIndexOperation = 'blockWrite' | 'unfreeze';
|
|
@ -1,9 +1,7 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/upgrade-assistant-plugin",
|
||||
"owner": [
|
||||
"@elastic/kibana-core"
|
||||
],
|
||||
"owner": "@elastic/kibana-management",
|
||||
"group": "platform",
|
||||
"visibility": "private",
|
||||
"plugin": {
|
||||
|
|
|
@ -11,10 +11,25 @@
|
|||
margin-right: $euiSizeM;
|
||||
}
|
||||
|
||||
$stepStatusToCallOutColor: (
|
||||
failed: $euiColorDanger,
|
||||
complete: $euiColorSuccess,
|
||||
paused: $euiColorWarning,
|
||||
cancelled: $euiColorWarning,
|
||||
);
|
||||
|
||||
.upgStepProgress__status--circle {
|
||||
text-align: center;
|
||||
border-radius: $euiSizeM;
|
||||
line-height: $euiSize - 2px;
|
||||
|
||||
@each $status, $callOutColor in $stepStatusToCallOutColor {
|
||||
&-#{$status} {
|
||||
$statusBg: tintOrShade($callOutColor, 90%, 70%);
|
||||
color: shadeOrTint(makeHighContrastColor($callOutColor, $statusBg), 0, 20%);
|
||||
background-color: $statusBg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upgStepProgress__title {
|
|
@ -8,64 +8,38 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { Fragment, ReactNode } from 'react';
|
||||
|
||||
import { EuiIcon, EuiLoadingSpinner, useEuiTheme } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { EuiIcon, EuiLoadingSpinner } from '@elastic/eui';
|
||||
|
||||
import './_step_progress.scss';
|
||||
|
||||
type STATUS = 'incomplete' | 'inProgress' | 'complete' | 'failed' | 'paused' | 'cancelled';
|
||||
|
||||
const StepStatus: React.FunctionComponent<{ status: STATUS; idx: number }> = ({ status, idx }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
if (status === 'incomplete') {
|
||||
return <span className="upgStepProgress__status">{idx + 1}.</span>;
|
||||
} else if (status === 'inProgress') {
|
||||
return <EuiLoadingSpinner size="m" className="upgStepProgress__status" />;
|
||||
} else if (status === 'complete') {
|
||||
return (
|
||||
<span
|
||||
className="upgStepProgress__status upgStepProgress__status--circle"
|
||||
css={css`
|
||||
color: ${euiTheme.colors.textSuccess};
|
||||
background-color: ${euiTheme.colors.backgroundBaseSuccess};
|
||||
`}
|
||||
>
|
||||
<span className="upgStepProgress__status upgStepProgress__status--circle upgStepProgress__status--circle-complete">
|
||||
<EuiIcon type="check" size="s" />
|
||||
</span>
|
||||
);
|
||||
} else if (status === 'paused') {
|
||||
return (
|
||||
<span
|
||||
className="upgStepProgress__status upgStepProgress__status--circle"
|
||||
css={css`
|
||||
color: ${euiTheme.colors.textWarning};
|
||||
background-color: ${euiTheme.colors.backgroundBaseWarning};
|
||||
`}
|
||||
>
|
||||
<span className="upgStepProgress__status upgStepProgress__status--circle upgStepProgress__status--circle-paused">
|
||||
<EuiIcon type="pause" size="s" />
|
||||
</span>
|
||||
);
|
||||
} else if (status === 'cancelled') {
|
||||
return (
|
||||
<span
|
||||
className="upgStepProgress__status upgStepProgress__status--circle"
|
||||
css={css`
|
||||
color: ${euiTheme.colors.textWarning};
|
||||
background-color: ${euiTheme.colors.backgroundBaseWarning};
|
||||
`}
|
||||
>
|
||||
<span className="upgStepProgress__status upgStepProgress__status--circle upgStepProgress__status--circle-cancelled">
|
||||
<EuiIcon type="cross" size="s" />
|
||||
</span>
|
||||
);
|
||||
} else if (status === 'failed') {
|
||||
return (
|
||||
<span
|
||||
className="upgStepProgress__status upgStepProgress__status--circle"
|
||||
css={css`
|
||||
color: ${euiTheme.colors.textDanger};
|
||||
background-color: ${euiTheme.colors.backgroundBaseDanger};
|
||||
`}
|
||||
>
|
||||
<span className="upgStepProgress__status upgStepProgress__status--circle upgStepProgress__status--circle-failed">
|
||||
<EuiIcon type="cross" size="s" />
|
||||
</span>
|
||||
);
|
|
@ -8,22 +8,31 @@
|
|||
import React, { createContext, useContext } from 'react';
|
||||
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
import { useReindexStatus, ReindexState } from './use_reindex_state';
|
||||
import { useMigrationStatus, MigrationState } from './use_migration_state';
|
||||
import type { DataStreamResolutionType } from '../../../../../../common/types';
|
||||
|
||||
export interface ReindexStateContext {
|
||||
reindexState: ReindexState;
|
||||
startReindex: () => Promise<void>;
|
||||
export interface MigrationStateContext {
|
||||
loadDataStreamMetadata: () => Promise<void>;
|
||||
migrationState: MigrationState;
|
||||
|
||||
initMigration: (resolutionType: DataStreamResolutionType) => void;
|
||||
|
||||
// reindex resolution actions
|
||||
startReindex: () => Promise<void>;
|
||||
cancelReindex: () => Promise<void>;
|
||||
|
||||
// readonly resolution actions
|
||||
startReadonly: () => Promise<void>;
|
||||
cancelReadonly: () => Promise<void>;
|
||||
}
|
||||
|
||||
const DataStreamReindexContext = createContext<ReindexStateContext | undefined>(undefined);
|
||||
const DataStreamMigrationContext = createContext<MigrationStateContext | undefined>(undefined);
|
||||
|
||||
export const useDataStreamReindexContext = () => {
|
||||
const context = useContext(DataStreamReindexContext);
|
||||
export const useDataStreamMigrationContext = () => {
|
||||
const context = useContext(DataStreamMigrationContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
'useDataStreamReindexContext must be used within a <DataStreamReindexStatusProvider />'
|
||||
'useDataStreamMigrationContext must be used within a <DataStreamMigrationStatusProvider />'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
|
@ -35,26 +44,37 @@ interface Props {
|
|||
dataStreamName: string;
|
||||
}
|
||||
|
||||
export const DataStreamReindexStatusProvider: React.FunctionComponent<Props> = ({
|
||||
export const DataStreamMigrationStatusProvider: React.FunctionComponent<Props> = ({
|
||||
api,
|
||||
dataStreamName,
|
||||
children,
|
||||
}) => {
|
||||
const { reindexState, startReindex, loadDataStreamMetadata, cancelReindex } = useReindexStatus({
|
||||
const {
|
||||
migrationState,
|
||||
cancelReadonly,
|
||||
startReindex,
|
||||
loadDataStreamMetadata,
|
||||
cancelReindex,
|
||||
startReadonly,
|
||||
initMigration,
|
||||
} = useMigrationStatus({
|
||||
dataStreamName,
|
||||
api,
|
||||
});
|
||||
|
||||
return (
|
||||
<DataStreamReindexContext.Provider
|
||||
<DataStreamMigrationContext.Provider
|
||||
value={{
|
||||
reindexState,
|
||||
migrationState,
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
startReadonly,
|
||||
cancelReadonly,
|
||||
initMigration,
|
||||
loadDataStreamMetadata,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DataStreamReindexContext.Provider>
|
||||
</DataStreamMigrationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,29 +18,33 @@ import { METRIC_TYPE } from '@kbn/analytics';
|
|||
|
||||
import moment from 'moment';
|
||||
import numeral from '@elastic/numeral';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
DataStreamReindexStatus,
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamsAction,
|
||||
EnrichedDeprecationInfo,
|
||||
} from '../../../../../../../common/types';
|
||||
|
||||
import { ReindexStateContext } from '../context';
|
||||
import { MigrationStateContext } from '../context';
|
||||
|
||||
import { DeprecationBadge } from '../../../../shared';
|
||||
import {
|
||||
UIM_DATA_STREAM_REINDEX_START_CLICK,
|
||||
UIM_DATA_STREAM_REINDEX_STOP_CLICK,
|
||||
UIM_DATA_STREAM_START_READONLY_CLICK,
|
||||
UIM_DATA_STREAM_STOP_READONLY_CLICK,
|
||||
uiMetricService,
|
||||
} from '../../../../../lib/ui_metric';
|
||||
|
||||
import { containerMessages } from './messages';
|
||||
import type { FlyoutStep } from './steps/types';
|
||||
import { InitializingFlyoutStep } from './steps/initializing';
|
||||
import { ConfirmReindexingFlyoutStep } from './steps/confirm';
|
||||
import { ConfirmMigrationFlyoutStep } from './steps/confirm';
|
||||
import { DataStreamDetailsFlyoutStep } from './steps/details';
|
||||
import { ChecklistFlyoutStep } from './steps/checklist';
|
||||
import { ReindexingCompletedFlyoutStep } from './steps/completed';
|
||||
import { MigrationCompletedFlyoutStep } from './steps/completed';
|
||||
|
||||
interface Props extends ReindexStateContext {
|
||||
interface Props extends MigrationStateContext {
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
closeFlyout: () => void;
|
||||
}
|
||||
|
@ -51,30 +55,32 @@ const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b';
|
|||
export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
||||
cancelReindex,
|
||||
loadDataStreamMetadata,
|
||||
reindexState,
|
||||
migrationState,
|
||||
startReindex,
|
||||
startReadonly,
|
||||
initMigration,
|
||||
cancelReadonly,
|
||||
closeFlyout,
|
||||
deprecation,
|
||||
}) => {
|
||||
const { status, reindexWarnings, errorMessage, meta } = reindexState;
|
||||
const { index } = deprecation;
|
||||
|
||||
const { status, migrationWarnings, errorMessage, resolutionType, meta } = migrationState;
|
||||
const { index, correctiveAction } = deprecation;
|
||||
const [flyoutStep, setFlyoutStep] = useState<FlyoutStep>('initializing');
|
||||
|
||||
const switchFlyoutStep = useCallback(() => {
|
||||
switch (status) {
|
||||
case DataStreamReindexStatus.notStarted: {
|
||||
case DataStreamMigrationStatus.notStarted: {
|
||||
setFlyoutStep('notStarted');
|
||||
return;
|
||||
}
|
||||
case DataStreamReindexStatus.failed:
|
||||
case DataStreamReindexStatus.fetchFailed:
|
||||
case DataStreamReindexStatus.cancelled:
|
||||
case DataStreamReindexStatus.inProgress: {
|
||||
case DataStreamMigrationStatus.failed:
|
||||
case DataStreamMigrationStatus.fetchFailed:
|
||||
case DataStreamMigrationStatus.cancelled:
|
||||
case DataStreamMigrationStatus.inProgress: {
|
||||
setFlyoutStep('inProgress');
|
||||
return;
|
||||
}
|
||||
case DataStreamReindexStatus.completed: {
|
||||
case DataStreamMigrationStatus.completed: {
|
||||
setTimeout(() => {
|
||||
// wait for 1.5 more seconds fur the UI to visually get to 100%
|
||||
setFlyoutStep('completed');
|
||||
|
@ -97,11 +103,21 @@ export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
|||
await startReindex();
|
||||
}, [startReindex]);
|
||||
|
||||
const onStartReadonly = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_DATA_STREAM_START_READONLY_CLICK);
|
||||
await startReadonly();
|
||||
}, [startReadonly]);
|
||||
|
||||
const onStopReindex = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_DATA_STREAM_REINDEX_STOP_CLICK);
|
||||
await cancelReindex();
|
||||
}, [cancelReindex]);
|
||||
|
||||
const onStopReadonly = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_DATA_STREAM_STOP_READONLY_CLICK);
|
||||
await cancelReadonly();
|
||||
}, [cancelReadonly]);
|
||||
|
||||
const { docsSizeFormatted, indicesRequiringUpgradeDocsCount, lastIndexCreationDateFormatted } =
|
||||
useMemo(() => {
|
||||
if (!meta) {
|
||||
|
@ -143,77 +159,93 @@ export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
|||
|
||||
return (
|
||||
<DataStreamDetailsFlyoutStep
|
||||
correctiveAction={correctiveAction as DataStreamsAction}
|
||||
closeFlyout={closeFlyout}
|
||||
lastIndexCreationDateFormatted={lastIndexCreationDateFormatted}
|
||||
meta={meta}
|
||||
startReindex={() => {
|
||||
initAction={(selectedResolutionType) => {
|
||||
initMigration(selectedResolutionType);
|
||||
setFlyoutStep('confirm');
|
||||
}}
|
||||
reindexState={reindexState}
|
||||
lastIndexCreationDateFormatted={lastIndexCreationDateFormatted}
|
||||
meta={meta}
|
||||
migrationState={migrationState}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'confirm': {
|
||||
if (!meta) {
|
||||
if (!meta || !resolutionType) {
|
||||
return (
|
||||
<InitializingFlyoutStep
|
||||
errorMessage={errorMessage || containerMessages.errorLoadingDataStreamInfo}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmReindexingFlyoutStep
|
||||
warnings={reindexWarnings ?? []}
|
||||
<ConfirmMigrationFlyoutStep
|
||||
warnings={(migrationWarnings ?? []).filter(
|
||||
(warning) => warning.resolutionType === resolutionType
|
||||
)}
|
||||
meta={meta}
|
||||
resolutionType={resolutionType}
|
||||
hideWarningsStep={() => {
|
||||
setFlyoutStep('notStarted');
|
||||
}}
|
||||
continueReindex={() => {
|
||||
onStartReindex();
|
||||
startAction={() => {
|
||||
if (resolutionType === 'readonly') {
|
||||
onStartReadonly();
|
||||
} else {
|
||||
onStartReindex();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'inProgress': {
|
||||
if (!meta) {
|
||||
if (!meta || !resolutionType) {
|
||||
return (
|
||||
<InitializingFlyoutStep
|
||||
errorMessage={errorMessage || containerMessages.errorLoadingDataStreamInfo}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ChecklistFlyoutStep
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={() => {
|
||||
executeAction={() => {
|
||||
setFlyoutStep('confirm');
|
||||
}}
|
||||
reindexState={reindexState}
|
||||
cancelReindex={onStopReindex}
|
||||
resolutionType={resolutionType}
|
||||
migrationState={migrationState}
|
||||
cancelAction={() => {
|
||||
if (resolutionType === 'readonly') {
|
||||
onStopReadonly();
|
||||
} else {
|
||||
onStopReindex();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'completed': {
|
||||
if (!meta) {
|
||||
return (
|
||||
<InitializingFlyoutStep
|
||||
errorMessage={errorMessage || containerMessages.errorLoadingDataStreamInfo}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <ReindexingCompletedFlyoutStep meta={meta} />;
|
||||
return <MigrationCompletedFlyoutStep meta={meta} resolutionType={resolutionType} />;
|
||||
}
|
||||
}
|
||||
}, [
|
||||
flyoutStep,
|
||||
reindexState,
|
||||
migrationState,
|
||||
closeFlyout,
|
||||
onStartReindex,
|
||||
onStopReindex,
|
||||
lastIndexCreationDateFormatted,
|
||||
reindexWarnings,
|
||||
migrationWarnings,
|
||||
meta,
|
||||
errorMessage,
|
||||
onStartReadonly,
|
||||
onStopReadonly,
|
||||
resolutionType,
|
||||
initMigration,
|
||||
correctiveAction,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
@ -222,7 +254,7 @@ export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
|||
<EuiFlyoutHeader hasBorder>
|
||||
<DeprecationBadge
|
||||
isCritical={deprecation.isCritical}
|
||||
isResolved={status === DataStreamReindexStatus.completed}
|
||||
isResolved={status === DataStreamMigrationStatus.completed}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiTitle size="s" data-test-subj="flyoutTitle">
|
||||
|
@ -237,7 +269,12 @@ export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
|||
textStyle="reverse"
|
||||
listItems={[
|
||||
{
|
||||
title: 'Reindexing required for indices created on or before',
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.flyout.container.affectedIndicesCreatedOnOrBefore',
|
||||
{
|
||||
defaultMessage: 'Migration required for indices created on or before',
|
||||
}
|
||||
),
|
||||
description: lastIndexCreationDateFormatted,
|
||||
},
|
||||
]}
|
||||
|
@ -249,7 +286,12 @@ export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
|||
textStyle="reverse"
|
||||
listItems={[
|
||||
{
|
||||
title: 'Size',
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.flyout.container.indicesDocsSize',
|
||||
{
|
||||
defaultMessage: 'Size',
|
||||
}
|
||||
),
|
||||
description: docsSizeFormatted,
|
||||
},
|
||||
]}
|
||||
|
@ -260,7 +302,12 @@ export const DataStreamReindexFlyout: React.FunctionComponent<Props> = ({
|
|||
textStyle="reverse"
|
||||
listItems={[
|
||||
{
|
||||
title: 'Document Count',
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.flyout.container.indicesDocsCount',
|
||||
{
|
||||
defaultMessage: 'Document Count',
|
||||
}
|
||||
),
|
||||
description: indicesRequiringUpgradeDocsCount,
|
||||
},
|
||||
]}
|
||||
|
|
|
@ -8,37 +8,46 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DataStreamReindexStatus } from '../../../../../../../common/types';
|
||||
import {
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamResolutionType,
|
||||
} from '../../../../../../../common/types';
|
||||
|
||||
export const getPrimaryButtonLabel = (status?: DataStreamReindexStatus) => {
|
||||
export const getPrimaryButtonLabel = (
|
||||
status?: DataStreamMigrationStatus,
|
||||
resolutionType?: DataStreamResolutionType
|
||||
) => {
|
||||
switch (status) {
|
||||
case DataStreamReindexStatus.fetchFailed:
|
||||
case DataStreamReindexStatus.failed:
|
||||
case DataStreamMigrationStatus.fetchFailed:
|
||||
case DataStreamMigrationStatus.failed:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.reindexButton.tryAgainLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.reindexButton.tryAgainLabel"
|
||||
defaultMessage="Try again"
|
||||
/>
|
||||
);
|
||||
case DataStreamReindexStatus.inProgress:
|
||||
case DataStreamMigrationStatus.inProgress:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.reindexButton.reindexingLabel"
|
||||
defaultMessage="Reindexing…"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.reindexButton.reindexingLabel"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migrating}}…"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
case DataStreamReindexStatus.cancelled:
|
||||
case DataStreamMigrationStatus.cancelled:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.reindexButton.restartLabel"
|
||||
defaultMessage="Restart reindexing"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.reindexButton.restartLabel"
|
||||
defaultMessage="{resolutionType, select, reindex {Restart reindexing} readonly {Restart marking as read-only} other {Restart migration}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.reindexButton.runReindexLabel"
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.reindexButton.runReindexLabel"
|
||||
defaultMessage="{resolutionType, select, reindex {Start reindexing} readonly {Start marking as read-only} other {Start migration}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,10 +20,13 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { DataStreamReindexStatus } from '../../../../../../../../../common/types';
|
||||
import {
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamResolutionType,
|
||||
} from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex_state';
|
||||
import { ReindexProgress } from './progress';
|
||||
import type { MigrationState } from '../../../use_migration_state';
|
||||
import { MigrationProgress } from './progress';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
import { getPrimaryButtonLabel } from '../../messages';
|
||||
|
||||
|
@ -32,25 +35,26 @@ import { getPrimaryButtonLabel } from '../../messages';
|
|||
*/
|
||||
export const ChecklistFlyoutStep: React.FunctionComponent<{
|
||||
closeFlyout: () => void;
|
||||
reindexState: ReindexState;
|
||||
startReindex: () => void;
|
||||
cancelReindex: () => void;
|
||||
}> = ({ closeFlyout, reindexState, startReindex, cancelReindex }) => {
|
||||
migrationState: MigrationState;
|
||||
resolutionType: DataStreamResolutionType;
|
||||
executeAction: () => void;
|
||||
cancelAction: () => void;
|
||||
}> = ({ closeFlyout, migrationState, resolutionType, executeAction, cancelAction }) => {
|
||||
const {
|
||||
services: { api },
|
||||
} = useAppContext();
|
||||
|
||||
const { loadingState, status, hasRequiredPrivileges } = reindexState;
|
||||
const { loadingState, status, hasRequiredPrivileges } = migrationState;
|
||||
const loading =
|
||||
loadingState === LoadingState.Loading || status === DataStreamReindexStatus.inProgress;
|
||||
const isCompleted = status === DataStreamReindexStatus.completed;
|
||||
const hasFetchFailed = status === DataStreamReindexStatus.fetchFailed;
|
||||
const hasReindexingFailed = status === DataStreamReindexStatus.failed;
|
||||
loadingState === LoadingState.Loading || status === DataStreamMigrationStatus.inProgress;
|
||||
const isCompleted = status === DataStreamMigrationStatus.completed;
|
||||
const hasFetchFailed = status === DataStreamMigrationStatus.fetchFailed;
|
||||
const hasMigrationFailed = status === DataStreamMigrationStatus.failed;
|
||||
|
||||
const { data: nodes } = api.useLoadNodeDiskSpace();
|
||||
|
||||
const showMainButton = !hasFetchFailed && !isCompleted && hasRequiredPrivileges;
|
||||
const shouldShowCancelButton = showMainButton && status === DataStreamReindexStatus.inProgress;
|
||||
const shouldShowCancelButton = showMainButton && status === DataStreamMigrationStatus.inProgress;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -61,8 +65,8 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to reindex this index"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to migrate this data stream"
|
||||
/>
|
||||
}
|
||||
color="danger"
|
||||
|
@ -79,15 +83,15 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
data-test-subj="lowDiskSpaceCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.lowDiskSpaceCalloutTitle"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.lowDiskSpaceCalloutTitle"
|
||||
defaultMessage="Nodes with low disk space"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent reindexing. The following nodes are impacted:"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent migration. The following nodes are impacted:"
|
||||
/>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
@ -96,7 +100,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
{nodes.map(({ nodeName, available, nodeId }) => (
|
||||
<li key={nodeId} data-test-subj="impactedNodeListItem">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.lowDiskSpaceUsedText"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.lowDiskSpaceUsedText"
|
||||
defaultMessage="{nodeName} ({available} available)"
|
||||
values={{
|
||||
nodeName,
|
||||
|
@ -112,27 +116,30 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
</>
|
||||
)}
|
||||
|
||||
{(hasFetchFailed || hasReindexingFailed) && (
|
||||
{(hasFetchFailed || hasMigrationFailed) && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj={hasFetchFailed ? 'fetchFailedCallout' : 'reindexingFailedCallout'}
|
||||
data-test-subj={
|
||||
hasFetchFailed ? 'fetchFailedCallout' : 'dataStreamMigrationFailedCallout'
|
||||
}
|
||||
title={
|
||||
hasFetchFailed ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.fetchFailedCalloutTitle"
|
||||
defaultMessage="Reindex status not available"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.fetchFailedCalloutTitle"
|
||||
defaultMessage="Migration status not available"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingFailedCalloutTitle"
|
||||
defaultMessage="Reindexing error"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.migrationFailedCalloutTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} error"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
{reindexState.errorMessage}
|
||||
{migrationState.errorMessage}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
|
@ -142,19 +149,19 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Reindexing is performed in the background. You can return to the Upgrade Assistant to view progress."
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.readonlyCallout.backgroundResumeDetail"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.readonlyCallout.backgroundResumeDetail"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<ReindexProgress reindexState={reindexState} />
|
||||
<MigrationProgress migrationState={migrationState} />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.closeButtonLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
|
@ -167,13 +174,14 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
<EuiButton
|
||||
color={'accent'}
|
||||
iconType={'pause'}
|
||||
onClick={cancelReindex}
|
||||
onClick={cancelAction}
|
||||
disabled={!hasRequiredPrivileges}
|
||||
data-test-subj="cancelDataStreamReindexingButton"
|
||||
data-test-subj="cancelDataStreamMigrationButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.cancelReindexButtonLabel"
|
||||
defaultMessage="Cancel reindexing"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.cancelMigrationButtonLabel"
|
||||
defaultMessage="Cancel {resolutionType, select, reindex {reindexing} readonly {marking as read-only} other {migration}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
@ -183,12 +191,14 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
color={status === DataStreamReindexStatus.inProgress ? 'primary' : 'warning'}
|
||||
iconType={status === DataStreamReindexStatus.inProgress ? undefined : 'refresh'}
|
||||
onClick={startReindex}
|
||||
color={status === DataStreamMigrationStatus.inProgress ? 'primary' : 'warning'}
|
||||
iconType={
|
||||
status === DataStreamMigrationStatus.inProgress ? undefined : 'refresh'
|
||||
}
|
||||
onClick={executeAction}
|
||||
isLoading={loading}
|
||||
disabled={loading || !hasRequiredPrivileges}
|
||||
data-test-subj="startDataStreamReindexingButton"
|
||||
data-test-subj="startDataStreamMigrationButton"
|
||||
>
|
||||
{getPrimaryButtonLabel(status)}
|
||||
</EuiButton>
|
||||
|
|
|
@ -12,37 +12,39 @@ import { FormattedMessage, FormattedRelativeTime } from '@kbn/i18n-react';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import moment from 'moment';
|
||||
import { DataStreamReindexStatus } from '../../../../../../../../../common/types';
|
||||
import type { ReindexState } from '../../../use_reindex_state';
|
||||
import { StepProgress, StepProgressStep } from '../../../../reindex/flyout/step_progress';
|
||||
import { DataStreamMigrationStatus } from '../../../../../../../../../common/types';
|
||||
import type { MigrationState } from '../../../use_migration_state';
|
||||
import { getDataStreamReindexProgress } from '../../../../../../../lib/utils';
|
||||
import { ReindexingDocumentsStepTitle } from './progress_title';
|
||||
import { MigrateDocumentsStepTitle } from './progress_title';
|
||||
import { CancelLoadingState } from '../../../../../../types';
|
||||
import { StepProgress, type StepProgressStep } from '../../../../../common/step_progress';
|
||||
|
||||
interface Props {
|
||||
reindexState: ReindexState;
|
||||
migrationState: MigrationState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a list of steps in the reindex operation, the current status, a progress bar,
|
||||
* and any error messages that are encountered.
|
||||
*/
|
||||
export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
||||
const { status, reindexTaskPercComplete, cancelLoadingState, taskStatus } = props.reindexState;
|
||||
export const MigrationProgress: React.FunctionComponent<Props> = (props) => {
|
||||
const { status, taskPercComplete, cancelLoadingState, taskStatus, resolutionType } =
|
||||
props.migrationState;
|
||||
|
||||
// The reindexing step is special because it generally lasts longer and can be cancelled mid-flight
|
||||
const reindexingDocsStep = {
|
||||
title: (
|
||||
<EuiFlexGroup component="span">
|
||||
<EuiFlexItem grow={false}>
|
||||
<ReindexingDocumentsStepTitle {...props} />
|
||||
<MigrateDocumentsStepTitle {...props} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
} as StepProgressStep;
|
||||
|
||||
const inProgress =
|
||||
status === DataStreamReindexStatus.inProgress || status === DataStreamReindexStatus.completed;
|
||||
status === DataStreamMigrationStatus.inProgress ||
|
||||
status === DataStreamMigrationStatus.completed;
|
||||
|
||||
let euiProgressColor = 'subdued';
|
||||
|
||||
|
@ -55,21 +57,21 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
) {
|
||||
reindexingDocsStep.status = 'inProgress';
|
||||
euiProgressColor = 'subdued';
|
||||
} else if (status === DataStreamReindexStatus.failed) {
|
||||
} else if (status === DataStreamMigrationStatus.failed) {
|
||||
reindexingDocsStep.status = 'failed';
|
||||
euiProgressColor = 'danger';
|
||||
} else if (
|
||||
status === DataStreamReindexStatus.cancelled ||
|
||||
status === DataStreamMigrationStatus.cancelled ||
|
||||
cancelLoadingState === CancelLoadingState.Success
|
||||
) {
|
||||
reindexingDocsStep.status = 'cancelled';
|
||||
} else if (status === undefined) {
|
||||
reindexingDocsStep.status = 'incomplete';
|
||||
euiProgressColor = 'subdued';
|
||||
} else if (status === DataStreamReindexStatus.inProgress) {
|
||||
} else if (status === DataStreamMigrationStatus.inProgress) {
|
||||
reindexingDocsStep.status = 'inProgress';
|
||||
euiProgressColor = 'primary';
|
||||
} else if (status === DataStreamReindexStatus.completed) {
|
||||
} else if (status === DataStreamMigrationStatus.completed) {
|
||||
reindexingDocsStep.status = 'complete';
|
||||
euiProgressColor = 'success';
|
||||
} else {
|
||||
|
@ -79,7 +81,7 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
}
|
||||
|
||||
const progressPercentage = inProgress
|
||||
? getDataStreamReindexProgress(status, reindexTaskPercComplete)
|
||||
? getDataStreamReindexProgress(status, taskPercComplete)
|
||||
: undefined;
|
||||
const showProgressValueText = inProgress;
|
||||
const progressMaxValue = inProgress ? 100 : undefined;
|
||||
|
@ -89,15 +91,17 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
<EuiFlexItem>
|
||||
<EuiTitle size="xs" data-test-subj="reindexChecklistTitle">
|
||||
<h3>
|
||||
{status === DataStreamReindexStatus.inProgress ? (
|
||||
{status === DataStreamMigrationStatus.inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingInProgressTitle"
|
||||
defaultMessage="Reindexing in progress…"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingInProgressTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} in progress…"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklistTitle"
|
||||
defaultMessage="Reindex data stream"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklistTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindex data stream} readonly {Mark data stream as read-only} other {Migrate data stream}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
)}
|
||||
</h3>
|
||||
|
@ -110,7 +114,7 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
label={
|
||||
taskStatus ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingInProgressTitle"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingInProgressTitle"
|
||||
defaultMessage="Started {startTimeFromNow}"
|
||||
values={{
|
||||
startTimeFromNow: (
|
||||
|
@ -139,7 +143,7 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
{!taskStatus && (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.fetchingStatus"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.fetchingStatus"
|
||||
defaultMessage="Fetching Status…"
|
||||
/>
|
||||
</p>
|
||||
|
@ -151,11 +155,11 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
<EuiText size="s" color="danger">
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.progressStep.failedTitle',
|
||||
'xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.progressStep.failedTitle',
|
||||
{
|
||||
defaultMessage:
|
||||
'{count, plural, =1 {# Index} other {# Indices}} failed to reindex.',
|
||||
values: { count: taskStatus.errorsCount },
|
||||
'{count, plural, =1 {# Index} other {# Indices}} failed to get {resolutionType, select, reindex {reindexed} readonly {marked as read-only} other {migrated}}.',
|
||||
values: { count: taskStatus.errorsCount, resolutionType },
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
|
@ -166,11 +170,11 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
<EuiText size="s" color="success">
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.progressStep.completeTitle',
|
||||
'xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.progressStep.completeTitle',
|
||||
{
|
||||
defaultMessage:
|
||||
'{count, plural, =1 {# Index} other {# Indices}} successfully reindexed.',
|
||||
values: { count: taskStatus.successCount },
|
||||
'{count, plural, =1 {# Index} other {# Indices}} successfully {resolutionType, select, reindex {reindexed} readonly {marked as read-only} other {migrated}}.',
|
||||
values: { count: taskStatus.successCount, resolutionType },
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
|
@ -180,11 +184,11 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
<EuiText size="s" color="primary">
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.progressStep.inProgressTitle',
|
||||
'xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.progressStep.inProgressTitle',
|
||||
{
|
||||
defaultMessage:
|
||||
'{count, plural, =1 {# Index} other {# Indices}} currently reindexing.',
|
||||
values: { count: taskStatus.inProgressCount },
|
||||
'{count, plural, =1 {# Index} other {# Indices}} currently getting {resolutionType, select, reindex {reindexed} readonly {marked as read-only} other {migrated}}.',
|
||||
values: { count: taskStatus.inProgressCount, resolutionType },
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
|
@ -194,7 +198,7 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
<EuiText size="s">
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.progressStep.pendingTitle',
|
||||
'xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.progressStep.pendingTitle',
|
||||
{
|
||||
defaultMessage:
|
||||
'{count, plural, =1 {# Index} other {# Indices}} waiting to start.',
|
||||
|
|
|
@ -9,18 +9,18 @@ import React from 'react';
|
|||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { CancelLoadingState } from '../../../../../../types';
|
||||
import { DataStreamReindexStatus } from '../../../../../../../../../common/types';
|
||||
import type { ReindexState } from '../../../use_reindex_state';
|
||||
import { DataStreamMigrationStatus } from '../../../../../../../../../common/types';
|
||||
import type { MigrationState } from '../../../use_migration_state';
|
||||
|
||||
export const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
||||
reindexState: ReindexState;
|
||||
}> = ({ reindexState: { status, cancelLoadingState } }) => {
|
||||
export const MigrateDocumentsStepTitle: React.FunctionComponent<{
|
||||
migrationState: MigrationState;
|
||||
}> = ({ migrationState: { status, cancelLoadingState, resolutionType } }) => {
|
||||
switch (cancelLoadingState) {
|
||||
case CancelLoadingState.Requested:
|
||||
case CancelLoadingState.Loading: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.cancellingLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.cancelButton.cancellingLabel"
|
||||
defaultMessage="Cancelling…"
|
||||
/>
|
||||
);
|
||||
|
@ -28,7 +28,7 @@ export const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
case CancelLoadingState.Success: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.cancelledLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.cancelButton.cancelledLabel"
|
||||
defaultMessage="Cancelled"
|
||||
/>
|
||||
);
|
||||
|
@ -36,56 +36,61 @@ export const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
case CancelLoadingState.Error: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.errorLabel"
|
||||
defaultMessage="Failed to cancel reindexing"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.cancelButton.errorLabel"
|
||||
defaultMessage="Failed to cancel {resolutionType, select, reindex {reindexing} readonly {read-only} other {}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case DataStreamReindexStatus.inProgress: {
|
||||
case DataStreamMigrationStatus.inProgress: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindexing data stream"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.inProgress.reindexingDocumentsStepTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case DataStreamReindexStatus.failed:
|
||||
case DataStreamMigrationStatus.failed:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.failed.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindexing failed"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.failed.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Failed to {resolutionType, select, reindex {reindex} readonly {mark as read-only} other {}}"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
case DataStreamReindexStatus.fetchFailed:
|
||||
case DataStreamMigrationStatus.fetchFailed:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.fetchFailed.reindexingDocumentsStepTitle"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.fetchFailed.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Fetching status failed"
|
||||
/>
|
||||
);
|
||||
case DataStreamReindexStatus.cancelled:
|
||||
case DataStreamMigrationStatus.cancelled:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.cancelled.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindexing cancelled"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.cancelled.reindexingDocumentsStepTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {}} cancelled"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
case DataStreamReindexStatus.completed:
|
||||
case DataStreamMigrationStatus.completed:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.completed.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindexing completed"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.completed.reindexingDocumentsStepTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {}} completed"
|
||||
values={{ resolutionType }}
|
||||
/>
|
||||
);
|
||||
case DataStreamReindexStatus.notStarted:
|
||||
case DataStreamMigrationStatus.notStarted:
|
||||
default: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindex data stream"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.reindexingChecklist.inProgress.reindexingDocumentsStepTitle"
|
||||
defaultMessage="{resolutionType, select, reindex {Reindex data stream} readonly {Mark data stream as read-only} other {Unknown action}}"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,30 +9,37 @@ import React from 'react';
|
|||
|
||||
import { EuiFlyoutBody, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { DataStreamMetadata } from '../../../../../../../../../common/types';
|
||||
import type {
|
||||
DataStreamMetadata,
|
||||
DataStreamResolutionType,
|
||||
} from '../../../../../../../../../common/types';
|
||||
|
||||
interface Props {
|
||||
meta: DataStreamMetadata;
|
||||
meta?: DataStreamMetadata | null;
|
||||
resolutionType?: DataStreamResolutionType;
|
||||
}
|
||||
|
||||
export const ReindexingCompletedFlyoutStep: React.FunctionComponent<Props> = ({ meta }: Props) => {
|
||||
export const MigrationCompletedFlyoutStep: React.FunctionComponent<Props> = ({
|
||||
meta,
|
||||
resolutionType,
|
||||
}: Props) => {
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="Data Stream Reindexing Complete"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="Data Stream Migration Complete"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="Success! {count, plural, =1 {# backing index} other {# backing indices}} successfully reindexed."
|
||||
values={{ count: meta.indicesRequiringUpgradeCount }}
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="Success! {count, plural, =0 {backing indices} =1 {# backing index} other {# backing indices}} successfully {resolutionType, select, reindex {reindexed} readonly {marked as read-only} other {migrated}}."
|
||||
values={{ count: meta?.indicesRequiringUpgradeCount || 0, resolutionType }}
|
||||
/>
|
||||
</p>
|
||||
</EuiFlyoutBody>
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { ReindexingCompletedFlyoutStep } from './completed_step';
|
||||
export { MigrationCompletedFlyoutStep } from './completed_step';
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
|
||||
export const ReindexWarningCallout: React.FunctionComponent<{}> = () => {
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.reindex.calloutTitle"
|
||||
defaultMessage="This operation requires destructive changes that cannot be reversed"
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.reindex.calloutDetail"
|
||||
defaultMessage="Ensure data has been backed up before continuing. To proceed with reindexing this data, confirm below."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
||||
|
||||
export const ReadonlyWarningCallout: React.FunctionComponent<{}> = () => {
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.readonly.calloutTitle"
|
||||
defaultMessage="Marking this data read-only could affect some of the existing setups"
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.readonly.calloutDetail"
|
||||
defaultMessage="Make sure you have backed up your data, etc. You can always re-index this data later to make it editable."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -10,59 +10,60 @@ import React, { useState } from 'react';
|
|||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
DataStreamReindexWarning,
|
||||
DataStreamReindexWarningTypes,
|
||||
DataStreamMigrationWarning,
|
||||
DataStreamWarningTypes,
|
||||
DataStreamMetadata,
|
||||
DataStreamResolutionType,
|
||||
} from '../../../../../../../../../common/types';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
import {
|
||||
IncompatibleDataInDataStreamWarningCheckbox,
|
||||
WarningCheckboxProps,
|
||||
} from './warning_step_checkbox';
|
||||
AffectExistingSetupsWarningCheckbox,
|
||||
} from './warnings';
|
||||
|
||||
import { ReindexWarningCallout, ReadonlyWarningCallout } from './callouts';
|
||||
interface CheckedIds {
|
||||
[id: string]: boolean;
|
||||
}
|
||||
|
||||
const warningToComponentMap: Record<
|
||||
DataStreamReindexWarningTypes,
|
||||
DataStreamWarningTypes,
|
||||
React.FunctionComponent<WarningCheckboxProps>
|
||||
> = {
|
||||
incompatibleDataStream: IncompatibleDataInDataStreamWarningCheckbox,
|
||||
affectExistingSetups: AffectExistingSetupsWarningCheckbox,
|
||||
};
|
||||
|
||||
export const idForWarning = (id: number) => `reindexWarning-${id}`;
|
||||
interface WarningsConfirmationFlyoutProps {
|
||||
hideWarningsStep: () => void;
|
||||
continueReindex: () => void;
|
||||
warnings: DataStreamReindexWarning[];
|
||||
meta: DataStreamMetadata;
|
||||
}
|
||||
export const idForWarning = (id: number) => `migrationWarning-${id}`;
|
||||
|
||||
/**
|
||||
* Displays warning text about destructive changes required to reindex this index. The user
|
||||
* Displays warning text about changes required to migrate this data stream. The user
|
||||
* must acknowledge each change before being allowed to proceed.
|
||||
*/
|
||||
export const ConfirmReindexingFlyoutStep: React.FunctionComponent<
|
||||
WarningsConfirmationFlyoutProps
|
||||
> = ({ warnings, hideWarningsStep, continueReindex, meta }) => {
|
||||
export const ConfirmMigrationFlyoutStep: React.FunctionComponent<{
|
||||
hideWarningsStep: () => void;
|
||||
startAction: () => void;
|
||||
resolutionType: DataStreamResolutionType;
|
||||
warnings: DataStreamMigrationWarning[];
|
||||
meta: DataStreamMetadata;
|
||||
}> = ({ warnings, hideWarningsStep, startAction, resolutionType, meta }) => {
|
||||
const {
|
||||
services: {
|
||||
core: { docLinks },
|
||||
},
|
||||
} = useAppContext();
|
||||
const { links } = docLinks;
|
||||
|
||||
const [checkedIds, setCheckedIds] = useState<CheckedIds>(
|
||||
warnings.reduce((initialCheckedIds, warning, index) => {
|
||||
initialCheckedIds[idForWarning(index)] = false;
|
||||
|
@ -84,36 +85,75 @@ export const ConfirmReindexingFlyoutStep: React.FunctionComponent<
|
|||
}));
|
||||
};
|
||||
|
||||
const startActionButtonLabel =
|
||||
resolutionType === 'reindex'
|
||||
? i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.startActionButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Start reindexing',
|
||||
}
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.startActionButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Mark all read-only',
|
||||
}
|
||||
);
|
||||
|
||||
const actionClarification =
|
||||
resolutionType === 'reindex' ? (
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.flyout.warningsStep.reindex.acceptChangesTitle"
|
||||
defaultMessage="{count, plural, =1 {# backing index} other {# backing indices}}, including current write index, will be re-indexed. Current write index will be rolled over first."
|
||||
values={{
|
||||
count: meta.indicesRequiringUpgradeCount,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<EuiSpacer size="s" />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.flyout.warningsStep.reindex.acceptChangesTitle"
|
||||
defaultMessage="You can increase the speed of reindexing by changing throttling configuration on ES. Where changing throttling configuration allows you to utilize more resources to speed up the reindexing process. {learnMoreHtml}"
|
||||
values={{
|
||||
learnMoreHtml: (
|
||||
<EuiLink
|
||||
href={`${links.elasticsearch.docsBase}data-stream-reindex-api.html#reindex-data-stream-api-settings`}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.learnMoreLink"
|
||||
defaultMessage="Learn more"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.flyout.warningsStep.readonly.acceptChangesTitle"
|
||||
defaultMessage="{count, plural, =1 {# backing index} other {# backing indices}}, including current write index, will be marked as read-only."
|
||||
values={{
|
||||
count: meta.indicesRequiringUpgradeCount,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutBody>
|
||||
{warnings.length > 0 && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.destructiveCallout.calloutTitle"
|
||||
defaultMessage="This operation requires destructive changes that cannot be reversed"
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.destructiveCallout.calloutDetail"
|
||||
defaultMessage="Ensure data has been backed up before continuing. To proceed with reindexing this data, confirm below."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
{resolutionType === 'reindex' && <ReindexWarningCallout />}
|
||||
{resolutionType === 'readonly' && <ReadonlyWarningCallout />}
|
||||
<EuiSpacer />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.dataStreamReindexing.flyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="{count, plural, =1 {# backing index} other {# backing indices}}, including current write index, will be re-indexed. Current write index will be rolled over first."
|
||||
values={{ count: meta.indicesRequiringUpgradeCount }}
|
||||
/>
|
||||
</p>
|
||||
{actionClarification}
|
||||
<EuiSpacer size="m" />
|
||||
{warnings.map((warning, index) => {
|
||||
const WarningCheckbox = warningToComponentMap[warning.warningType];
|
||||
|
@ -137,17 +177,14 @@ export const ConfirmReindexingFlyoutStep: React.FunctionComponent<
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="arrowLeft" onClick={hideWarningsStep} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.backButtonLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.backButtonLabel"
|
||||
defaultMessage="Back"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton fill color="primary" onClick={continueReindex} disabled={blockAdvance}>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.checklistStep.startReindexingButtonLabel"
|
||||
defaultMessage="Start reindexing"
|
||||
/>
|
||||
<EuiButton fill color="primary" onClick={startAction} disabled={blockAdvance}>
|
||||
{startActionButtonLabel}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { ConfirmReindexingFlyoutStep } from './confirm_step';
|
||||
export { ConfirmMigrationFlyoutStep } from './confirm_step';
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { WarningCheckbox, WarningCheckboxProps } from './warning_step_checkbox';
|
||||
|
||||
export const AffectExistingSetupsWarningCheckbox: React.FunctionComponent<WarningCheckboxProps> = ({
|
||||
isChecked,
|
||||
onChange,
|
||||
id,
|
||||
}) => {
|
||||
return (
|
||||
<WarningCheckbox
|
||||
isChecked={isChecked}
|
||||
onChange={onChange}
|
||||
warningId={id}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.affectExistingSetupsWarningTitle"
|
||||
defaultMessage="Mark as read-only all incompatible data for this data stream"
|
||||
/>
|
||||
}
|
||||
description={null}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { WarningCheckbox, WarningCheckboxProps } from './warning_step_checkbox';
|
||||
|
||||
export const IncompatibleDataInDataStreamWarningCheckbox: React.FunctionComponent<
|
||||
WarningCheckboxProps
|
||||
> = ({ isChecked, onChange, id }) => {
|
||||
return (
|
||||
<WarningCheckbox
|
||||
isChecked={isChecked}
|
||||
onChange={onChange}
|
||||
warningId={id}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.incompatibleDataWarningTitle"
|
||||
defaultMessage="Reindex all incompatible data for this data stream"
|
||||
/>
|
||||
}
|
||||
description={null}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { IncompatibleDataInDataStreamWarningCheckbox } from './incompatible_data';
|
||||
|
||||
export { AffectExistingSetupsWarningCheckbox } from './existing_setups';
|
||||
|
||||
export type { WarningCheckboxProps } from './warning_step_checkbox';
|
|
@ -18,19 +18,8 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { DocLinksStart } from '@kbn/core/public';
|
||||
import {
|
||||
DataStreamReindexWarning,
|
||||
DataStreamReindexWarningTypes,
|
||||
} from '../../../../../../../../../common/types';
|
||||
|
||||
export const hasReindexWarning = (
|
||||
warnings: DataStreamReindexWarning[],
|
||||
warningType: DataStreamReindexWarningTypes
|
||||
): boolean => {
|
||||
return Boolean(warnings.find((warning) => warning.warningType === warningType));
|
||||
};
|
||||
|
||||
const WarningCheckbox: React.FunctionComponent<{
|
||||
export const WarningCheckbox: React.FunctionComponent<{
|
||||
isChecked: boolean;
|
||||
warningId: string;
|
||||
label: React.ReactNode;
|
||||
|
@ -55,7 +44,7 @@ const WarningCheckbox: React.FunctionComponent<{
|
|||
<EuiIconTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.documentationLinkLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.documentationLinkLabel"
|
||||
defaultMessage="Documentation"
|
||||
/>
|
||||
}
|
||||
|
@ -82,22 +71,3 @@ export interface WarningCheckboxProps {
|
|||
docLinks: DocLinksStart['links'];
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const IncompatibleDataInDataStreamWarningCheckbox: React.FunctionComponent<
|
||||
WarningCheckboxProps
|
||||
> = ({ isChecked, onChange, id }) => {
|
||||
return (
|
||||
<WarningCheckbox
|
||||
isChecked={isChecked}
|
||||
onChange={onChange}
|
||||
warningId={id}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.incompatibleDataWarningTitle"
|
||||
defaultMessage="Reindex all incompatible data for this data stream"
|
||||
/>
|
||||
}
|
||||
description={null}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -23,13 +23,14 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import {
|
||||
DataStreamMetadata,
|
||||
DataStreamReindexStatus,
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamResolutionType,
|
||||
DataStreamsAction,
|
||||
} from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex_state';
|
||||
import type { MigrationState } from '../../../use_migration_state';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
import { DurationClarificationCallOut } from './warnings_callout';
|
||||
import { getPrimaryButtonLabel } from '../../messages';
|
||||
|
||||
/**
|
||||
* Displays a flyout that shows the current reindexing status for a given index.
|
||||
|
@ -37,11 +38,19 @@ import { getPrimaryButtonLabel } from '../../messages';
|
|||
|
||||
export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
||||
closeFlyout: () => void;
|
||||
reindexState: ReindexState;
|
||||
startReindex: () => void;
|
||||
migrationState: MigrationState;
|
||||
correctiveAction: DataStreamsAction;
|
||||
initAction: (resolutionType: DataStreamResolutionType) => void;
|
||||
lastIndexCreationDateFormatted: string;
|
||||
meta: DataStreamMetadata;
|
||||
}> = ({ closeFlyout, reindexState, startReindex, lastIndexCreationDateFormatted, meta }) => {
|
||||
}> = ({
|
||||
closeFlyout,
|
||||
migrationState,
|
||||
initAction,
|
||||
lastIndexCreationDateFormatted,
|
||||
correctiveAction,
|
||||
meta,
|
||||
}) => {
|
||||
const {
|
||||
services: {
|
||||
api,
|
||||
|
@ -49,12 +58,15 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
},
|
||||
} = useAppContext();
|
||||
|
||||
const { loadingState, status, hasRequiredPrivileges } = reindexState;
|
||||
const { loadingState, status, hasRequiredPrivileges } = migrationState;
|
||||
const loading =
|
||||
loadingState === LoadingState.Loading || status === DataStreamReindexStatus.inProgress;
|
||||
const isCompleted = status === DataStreamReindexStatus.completed;
|
||||
const hasFetchFailed = status === DataStreamReindexStatus.fetchFailed;
|
||||
const hasReindexingFailed = status === DataStreamReindexStatus.failed;
|
||||
loadingState === LoadingState.Loading || status === DataStreamMigrationStatus.inProgress;
|
||||
const isCompleted = status === DataStreamMigrationStatus.completed;
|
||||
const hasFetchFailed = status === DataStreamMigrationStatus.fetchFailed;
|
||||
const hasMigrationFailed = status === DataStreamMigrationStatus.failed;
|
||||
|
||||
const readOnlyExcluded = correctiveAction.metadata.excludedActions?.includes('readOnly');
|
||||
const reindexExcluded = correctiveAction.metadata.excludedActions?.includes('reindex');
|
||||
|
||||
const { data: nodes } = api.useLoadNodeDiskSpace();
|
||||
|
||||
|
@ -73,8 +85,8 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to reindex this data stream."
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to migrate this data stream."
|
||||
/>
|
||||
}
|
||||
color="danger"
|
||||
|
@ -91,15 +103,15 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
data-test-subj="lowDiskSpaceCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.lowDiskSpaceCalloutTitle"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.lowDiskSpaceCalloutTitle"
|
||||
defaultMessage="Nodes with low disk space"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent reindexing. The following nodes are impacted:"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent migration. The following nodes are impacted:"
|
||||
/>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
@ -108,7 +120,7 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
{nodes.map(({ nodeName, available, nodeId }) => (
|
||||
<li key={nodeId} data-test-subj="impactedNodeListItem">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.lowDiskSpaceUsedText"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.lowDiskSpaceUsedText"
|
||||
defaultMessage="{nodeName} ({available} available)"
|
||||
values={{
|
||||
nodeName,
|
||||
|
@ -124,27 +136,27 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
</>
|
||||
)}
|
||||
|
||||
{(hasFetchFailed || hasReindexingFailed) && (
|
||||
{(hasFetchFailed || hasMigrationFailed) && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj={hasFetchFailed ? 'fetchFailedCallout' : 'reindexingFailedCallout'}
|
||||
data-test-subj={hasFetchFailed ? 'fetchFailedCallout' : 'migrationFailedCallout'}
|
||||
title={
|
||||
hasFetchFailed ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.fetchFailedCalloutTitle"
|
||||
defaultMessage="Data stream reindex status not available"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.fetchFailedCalloutTitle"
|
||||
defaultMessage="Data stream migration status not available"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.reindexingFailedCalloutTitle"
|
||||
defaultMessage="Data stream reindexing error"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.migrationFailedCalloutTitle"
|
||||
defaultMessage="Data stream migration error"
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
{reindexState.errorMessage}
|
||||
{migrationState.errorMessage}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
|
@ -153,7 +165,7 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.notCompatibleIndicesText"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.notCompatibleIndicesText"
|
||||
defaultMessage="You have {backingIndicesCount} backing indices on this data stream that were created in ES 7.x and will not be compatible with next version."
|
||||
values={{
|
||||
backingIndicesCount: meta.indicesRequiringUpgradeCount,
|
||||
|
@ -162,7 +174,7 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.requiredUpgradeText"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.requiredUpgradeText"
|
||||
defaultMessage="{allBackingIndices} total backing indices, and {backingIndicesRequireingUpgrade} requires upgrade."
|
||||
values={{
|
||||
allBackingIndices: meta.allIndicesCount,
|
||||
|
@ -171,33 +183,38 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
/>
|
||||
</p>
|
||||
<ul>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.readOnlyText"
|
||||
tagName="li"
|
||||
defaultMessage="If you do not need to update historical data, mark as read-only. You can reindex post-upgrade if updates are needed."
|
||||
/>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.reindexOptionListTitle"
|
||||
defaultMessage="Reindex"
|
||||
/>
|
||||
<ul>
|
||||
{!readOnlyExcluded && (
|
||||
<li>
|
||||
<FormattedMessage
|
||||
tagName="li"
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.reindexOption.rolledOverIndex"
|
||||
defaultMessage="The current write index will be rolled over and reindexed."
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.readOnlyText"
|
||||
defaultMessage="If you do not need to update historical data, mark as read-only. You can reindex post-upgrade if updates are needed."
|
||||
/>
|
||||
</li>
|
||||
)}
|
||||
{!reindexExcluded && (
|
||||
<li>
|
||||
<FormattedMessage
|
||||
tagName="li"
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.reindexOption.additionalIndices"
|
||||
defaultMessage="Additional backing indices will be reindexed and remain editable."
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.reindexOptionListTitle"
|
||||
defaultMessage="Reindex"
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
<ul>
|
||||
<FormattedMessage
|
||||
tagName="li"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.reindexOption.rolledOverIndex"
|
||||
defaultMessage="The current write index will be rolled over and reindexed."
|
||||
/>
|
||||
<FormattedMessage
|
||||
tagName="li"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.reindexOption.additionalIndices"
|
||||
defaultMessage="Additional backing indices will be reindexed and remain editable."
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.reindexDescription"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.migrationDescription"
|
||||
defaultMessage="If you no longer need this data, you can also proceed by deleting these indices. {indexManagementLinkHtml}"
|
||||
values={{
|
||||
indexManagementLinkHtml: (
|
||||
|
@ -207,7 +224,7 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
)}`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.indexMgmtLink"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.indexMgmtLink"
|
||||
defaultMessage="Go to index management"
|
||||
/>
|
||||
</EuiLink>
|
||||
|
@ -223,24 +240,49 @@ export const DataStreamDetailsFlyoutStep: React.FunctionComponent<{
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.detailsStep.closeButtonLabel"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.detailsStep.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
{!hasFetchFailed && !isCompleted && hasRequiredPrivileges && (
|
||||
{!hasFetchFailed && !isCompleted && hasRequiredPrivileges && !reindexExcluded && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color={status === DataStreamReindexStatus.cancelled ? 'warning' : 'primary'}
|
||||
iconType={status === DataStreamReindexStatus.cancelled ? 'play' : undefined}
|
||||
onClick={startReindex}
|
||||
color={
|
||||
status === DataStreamMigrationStatus.cancelled
|
||||
? 'warning'
|
||||
: readOnlyExcluded
|
||||
? 'primary'
|
||||
: 'accent'
|
||||
}
|
||||
iconType={status === DataStreamMigrationStatus.cancelled ? 'play' : undefined}
|
||||
onClick={() => initAction('reindex')}
|
||||
isLoading={loading}
|
||||
disabled={loading || !hasRequiredPrivileges}
|
||||
data-test-subj="startReindexingButton"
|
||||
disabled={loading || !hasRequiredPrivileges || reindexExcluded}
|
||||
data-test-subj="startDataStreamReindexingButton"
|
||||
>
|
||||
{getPrimaryButtonLabel(status)}
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.reindexButton.initReindexButtonLabel"
|
||||
defaultMessage="Reindex"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{!readOnlyExcluded && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
color={'primary'}
|
||||
onClick={() => initAction('readonly')}
|
||||
disabled={!hasRequiredPrivileges || readOnlyExcluded}
|
||||
data-test-subj="startDataStreamReadonlyButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.checklistStep.initMarkReadOnlyButtonLabel"
|
||||
defaultMessage="Mark read-only"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
|
|
@ -22,20 +22,26 @@ export const DurationClarificationCallOut: React.FunctionComponent<Props> = ({
|
|||
<EuiCallOut color="primary">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.indicesNeedReindexing"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.indicesNeedReindexing"
|
||||
defaultMessage="Indices created on or before {formattedDate} need to be reindexed to a compatible format or marked as read-only."
|
||||
values={{ formattedDate }}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.suggestReadOnly"
|
||||
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}"
|
||||
values={{
|
||||
learnMoreHtml: (
|
||||
<EuiLink href={learnMoreUrl} target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.warningsStep.learnMoreLink"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.warningsStep.learnMoreLink"
|
||||
defaultMessage="Learn more"
|
||||
/>
|
||||
</EuiLink>
|
||||
|
|
|
@ -44,12 +44,12 @@ export const InitializingFlyoutStep: React.FunctionComponent<InitializingFlyoutS
|
|||
<EuiTitle size="s">
|
||||
{hasInitializingError ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.initializingStep.errorLoadingDataStreamInfo"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.initializingStep.errorLoadingDataStreamInfo"
|
||||
defaultMessage="Error loading data stream info"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.dataStream.reindexing.flyout.initializingStep.loadingDataStreamInfo"
|
||||
id="xpack.upgradeAssistant.dataStream.migration.flyout.initializingStep.loadingDataStreamInfo"
|
||||
defaultMessage="Loading Data stream info"
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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 { chunk } from 'lodash';
|
||||
import {
|
||||
DataStreamMetadata,
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamMigrationOperation,
|
||||
} from '../../../../../../common/types';
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
|
||||
interface ReadOnlyExecuteResponse {
|
||||
migrationOp: DataStreamMigrationOperation;
|
||||
}
|
||||
|
||||
const DEFAULT_BATCH_SIZE = 10;
|
||||
|
||||
export async function* readOnlyExecute(
|
||||
dataStreamName: string,
|
||||
meta: DataStreamMetadata | null,
|
||||
api: ApiService,
|
||||
batchSize: number = DEFAULT_BATCH_SIZE
|
||||
): AsyncGenerator<ReadOnlyExecuteResponse, ReadOnlyExecuteResponse, ReadOnlyExecuteResponse> {
|
||||
const { indicesRequiringUpgrade } = meta || {};
|
||||
|
||||
const startTimeMs = +Date.now();
|
||||
|
||||
if (!indicesRequiringUpgrade || !indicesRequiringUpgrade.length) {
|
||||
return {
|
||||
migrationOp: {
|
||||
status: DataStreamMigrationStatus.completed,
|
||||
resolutionType: 'readonly',
|
||||
taskPercComplete: 1,
|
||||
progressDetails: {
|
||||
startTimeMs,
|
||||
successCount: 0,
|
||||
pendingCount: 0,
|
||||
inProgressCount: 0,
|
||||
errorsCount: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let processedCount = 0;
|
||||
const batches = chunk(indicesRequiringUpgrade, batchSize);
|
||||
|
||||
try {
|
||||
for (const batch of batches) {
|
||||
const { error } = await api.markIndicesAsReadOnly(dataStreamName, batch);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
processedCount += batch.length;
|
||||
|
||||
const status =
|
||||
processedCount >= indicesRequiringUpgrade.length
|
||||
? DataStreamMigrationStatus.completed
|
||||
: DataStreamMigrationStatus.inProgress;
|
||||
const taskPercComplete = processedCount / indicesRequiringUpgrade.length;
|
||||
|
||||
yield {
|
||||
migrationOp: {
|
||||
resolutionType: 'readonly',
|
||||
status,
|
||||
taskPercComplete,
|
||||
progressDetails: {
|
||||
startTimeMs,
|
||||
successCount: processedCount,
|
||||
pendingCount: indicesRequiringUpgrade.length - processedCount,
|
||||
inProgressCount: batch.length,
|
||||
errorsCount: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
migrationOp: {
|
||||
resolutionType: 'readonly',
|
||||
status: DataStreamMigrationStatus.failed,
|
||||
errorMessage: error.message || 'Unknown error occurred',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
migrationOp: {
|
||||
resolutionType: 'readonly',
|
||||
status: DataStreamMigrationStatus.completed,
|
||||
taskPercComplete: 1,
|
||||
progressDetails: {
|
||||
startTimeMs,
|
||||
successCount: indicesRequiringUpgrade.length,
|
||||
pendingCount: 0,
|
||||
inProgressCount: 0,
|
||||
errorsCount: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
|
@ -16,81 +16,146 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { DataStreamReindexStatus } from '../../../../../../common/types';
|
||||
import {
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamResolutionType,
|
||||
DataStreamsAction,
|
||||
} from '../../../../../../common/types';
|
||||
import { getDataStreamReindexProgressLabel } from '../../../../lib/utils';
|
||||
import { LoadingState } from '../../../types';
|
||||
import { useDataStreamReindexContext } from './context';
|
||||
import { useDataStreamMigrationContext } from './context';
|
||||
|
||||
const i18nTexts = {
|
||||
reindexLoadingStatusText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.reindexLoadingStatusText',
|
||||
{
|
||||
defaultMessage: 'Loading status…',
|
||||
}
|
||||
),
|
||||
reindexInProgressText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.reindexInProgressText',
|
||||
{
|
||||
defaultMessage: 'Reindexing in progress…',
|
||||
}
|
||||
),
|
||||
reindexCompleteText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.reindexCompleteText',
|
||||
{
|
||||
defaultMessage: 'Reindex complete',
|
||||
}
|
||||
),
|
||||
reindexFailedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.reindexFailedText',
|
||||
{
|
||||
defaultMessage: 'Reindex failed',
|
||||
}
|
||||
),
|
||||
reindexFetchFailedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.reindexFetchFailedText',
|
||||
{
|
||||
defaultMessage: 'Reindex status not available',
|
||||
}
|
||||
),
|
||||
reindexCanceledText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.reindexCanceledText',
|
||||
{
|
||||
defaultMessage: 'Reindex cancelled',
|
||||
}
|
||||
),
|
||||
resolutionText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionLabel',
|
||||
{
|
||||
defaultMessage: 'Reindex',
|
||||
}
|
||||
),
|
||||
resolutionTooltipLabel: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'Resolve this issue by reindexing this data stream. This issue can be resolved automatically.',
|
||||
}
|
||||
),
|
||||
const getI18nTexts = (
|
||||
resolutionType?: DataStreamResolutionType,
|
||||
excludedActions: Array<'readOnly' | 'reindex'> = []
|
||||
) => {
|
||||
const resolutionAction = excludedActions.includes('readOnly')
|
||||
? 'reindex'
|
||||
: excludedActions.includes('reindex')
|
||||
? 'readOnly'
|
||||
: 'readOnlyOrReindex';
|
||||
|
||||
const resolutionTexts = {
|
||||
readOnlyOrReindex: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionReadOnlyOrReindexLabel',
|
||||
{
|
||||
defaultMessage: 'Mark as read-only, or reindex',
|
||||
}
|
||||
),
|
||||
readOnly: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionReadOnlyLabel',
|
||||
{
|
||||
defaultMessage: 'Mark as read-only',
|
||||
}
|
||||
),
|
||||
reindex: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionReindexLabel',
|
||||
{
|
||||
defaultMessage: 'Reindex',
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
const resolutionTooltipLabels = {
|
||||
readOnlyOrReindex: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipReadOnlyOrReindexLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'Resolve this issue by reindexing this data stream or marking its indices as read-only. This issue can be resolved automatically.',
|
||||
}
|
||||
),
|
||||
readOnly: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipReadOnlyLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'Resolve this issue by marking its indices as read-only. This issue can be resolved automatically.',
|
||||
}
|
||||
),
|
||||
reindex: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionTooltipReindexLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'Resolve this issue by reindexing this data stream. This issue can be resolved automatically.',
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
return {
|
||||
loadingStatusText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionLoadingStatusText',
|
||||
{
|
||||
defaultMessage: 'Loading status…',
|
||||
}
|
||||
),
|
||||
resolutionInProgressText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionInProgressText',
|
||||
{
|
||||
defaultMessage:
|
||||
'{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} in progress…',
|
||||
values: { resolutionType },
|
||||
}
|
||||
),
|
||||
resolutionCompleteText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionCompleteText',
|
||||
{
|
||||
defaultMessage:
|
||||
'{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} complete',
|
||||
values: { resolutionType },
|
||||
}
|
||||
),
|
||||
resolutionFailedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resulutionFailedText',
|
||||
{
|
||||
defaultMessage:
|
||||
'{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} failed',
|
||||
values: { resolutionType },
|
||||
}
|
||||
),
|
||||
resolutionFetchFailedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionFetchFailedText',
|
||||
{
|
||||
defaultMessage:
|
||||
'{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} status not available',
|
||||
values: { resolutionType },
|
||||
}
|
||||
),
|
||||
reindexCanceledText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.dataStream.resolutionCanceledText',
|
||||
{
|
||||
defaultMessage:
|
||||
'{resolutionType, select, reindex {Reindexing} readonly {Marking as read-only} other {Migration}} cancelled',
|
||||
values: { resolutionType },
|
||||
}
|
||||
),
|
||||
resolutionText: resolutionTexts[resolutionAction],
|
||||
resolutionTooltipLabel: resolutionTooltipLabels[resolutionAction],
|
||||
};
|
||||
};
|
||||
|
||||
export const DataStreamReindexResolutionCell: React.FunctionComponent = () => {
|
||||
const { reindexState } = useDataStreamReindexContext();
|
||||
export const DataStreamReindexResolutionCell: React.FunctionComponent<{
|
||||
correctiveAction: DataStreamsAction;
|
||||
}> = ({ correctiveAction }) => {
|
||||
const { migrationState } = useDataStreamMigrationContext();
|
||||
const i18nTexts = getI18nTexts(
|
||||
migrationState.resolutionType,
|
||||
correctiveAction.metadata.excludedActions
|
||||
);
|
||||
|
||||
if (reindexState.loadingState === LoadingState.Loading) {
|
||||
if (migrationState.loadingState === LoadingState.Loading) {
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.reindexLoadingStatusText}</EuiText>
|
||||
<EuiText size="s">{i18nTexts.loadingStatusText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
switch (reindexState.status) {
|
||||
case DataStreamReindexStatus.inProgress:
|
||||
switch (migrationState.status) {
|
||||
case DataStreamMigrationStatus.inProgress:
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -98,45 +163,45 @@ export const DataStreamReindexResolutionCell: React.FunctionComponent = () => {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">
|
||||
{i18nTexts.reindexInProgressText}{' '}
|
||||
{i18nTexts.resolutionInProgressText}{' '}
|
||||
{getDataStreamReindexProgressLabel(
|
||||
reindexState.status,
|
||||
reindexState.reindexTaskPercComplete
|
||||
migrationState.status,
|
||||
migrationState.taskPercComplete
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
case DataStreamReindexStatus.completed:
|
||||
case DataStreamMigrationStatus.completed:
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="check" color="success" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.reindexCompleteText}</EuiText>
|
||||
<EuiText size="s">{i18nTexts.resolutionCompleteText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
case DataStreamReindexStatus.failed:
|
||||
case DataStreamMigrationStatus.failed:
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="warning" color="danger" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.reindexFailedText}</EuiText>
|
||||
<EuiText size="s">{i18nTexts.resolutionFailedText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
case DataStreamReindexStatus.fetchFailed:
|
||||
case DataStreamMigrationStatus.fetchFailed:
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="warning" color="danger" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.reindexFetchFailedText}</EuiText>
|
||||
<EuiText size="s">{i18nTexts.resolutionFetchFailedText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { EuiTableRowCell } from '@elastic/eui';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { DataStreamsAction, EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { GlobalFlyout } from '../../../../../shared_imports';
|
||||
import { useAppContext } from '../../../../app_context';
|
||||
import {
|
||||
|
@ -20,7 +20,7 @@ import { DeprecationTableColumns } from '../../../types';
|
|||
import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells';
|
||||
import { DataStreamReindexResolutionCell } from './resolution_table_cell';
|
||||
import { DataStreamReindexFlyout } from './flyout';
|
||||
import { DataStreamReindexStatusProvider, useDataStreamReindexContext } from './context';
|
||||
import { DataStreamMigrationStatusProvider, useDataStreamMigrationContext } from './context';
|
||||
|
||||
const { useGlobalFlyout } = GlobalFlyout;
|
||||
|
||||
|
@ -34,7 +34,7 @@ const DataStreamTableRowCells: React.FunctionComponent<TableRowProps> = ({
|
|||
deprecation,
|
||||
}) => {
|
||||
const [showFlyout, setShowFlyout] = useState(false);
|
||||
const dataStreamContext = useDataStreamReindexContext();
|
||||
const dataStreamContext = useDataStreamMigrationContext();
|
||||
const { addContent: addContentToGlobalFlyout, removeContent: removeContentFromGlobalFlyout } =
|
||||
useGlobalFlyout();
|
||||
|
||||
|
@ -83,7 +83,11 @@ const DataStreamTableRowCells: React.FunctionComponent<TableRowProps> = ({
|
|||
fieldName={field}
|
||||
openFlyout={() => setShowFlyout(true)}
|
||||
deprecation={deprecation}
|
||||
resolutionTableCell={<DataStreamReindexResolutionCell />}
|
||||
resolutionTableCell={
|
||||
<DataStreamReindexResolutionCell
|
||||
correctiveAction={deprecation.correctiveAction as DataStreamsAction}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</EuiTableRowCell>
|
||||
);
|
||||
|
@ -98,8 +102,8 @@ export const DataStreamTableRow: React.FunctionComponent<TableRowProps> = (props
|
|||
} = useAppContext();
|
||||
|
||||
return (
|
||||
<DataStreamReindexStatusProvider dataStreamName={props.deprecation.index!} api={api}>
|
||||
<DataStreamMigrationStatusProvider dataStreamName={props.deprecation.index!} api={api}>
|
||||
<DataStreamTableRowCells {...props} />
|
||||
</DataStreamReindexStatusProvider>
|
||||
</DataStreamMigrationStatusProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* 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 { useRef, useCallback, useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamMigrationWarning,
|
||||
DataStreamMetadata,
|
||||
DataStreamReindexStatusResponse,
|
||||
DataStreamProgressDetails,
|
||||
DataStreamResolutionType,
|
||||
ResponseError,
|
||||
} from '../../../../../../common/types';
|
||||
import { CancelLoadingState, LoadingState } from '../../../types';
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
import { readOnlyExecute } from './readonly_state';
|
||||
|
||||
const POLL_INTERVAL = 1000;
|
||||
|
||||
export interface MigrationState {
|
||||
loadingState: LoadingState;
|
||||
cancelLoadingState?: CancelLoadingState;
|
||||
|
||||
resolutionType?: DataStreamResolutionType;
|
||||
status?: DataStreamMigrationStatus;
|
||||
taskPercComplete: number | null;
|
||||
errorMessage: string | null;
|
||||
migrationWarnings?: DataStreamMigrationWarning[];
|
||||
hasRequiredPrivileges?: boolean;
|
||||
taskStatus?: DataStreamProgressDetails;
|
||||
|
||||
meta: DataStreamMetadata | null;
|
||||
}
|
||||
|
||||
const getMigrationState = (
|
||||
migrationState: MigrationState,
|
||||
{
|
||||
migrationOp,
|
||||
warnings,
|
||||
hasRequiredPrivileges,
|
||||
meta: updatedMeta,
|
||||
}: DataStreamReindexStatusResponse & { meta?: DataStreamMetadata | null }
|
||||
) => {
|
||||
const newMigrationState: MigrationState = {
|
||||
...migrationState,
|
||||
// @ts-expect-error - resolutionType does non exist in all migration states.
|
||||
resolutionType: migrationOp?.resolutionType || migrationState.resolutionType,
|
||||
meta: updatedMeta || migrationState.meta,
|
||||
loadingState: LoadingState.Success,
|
||||
};
|
||||
|
||||
if (warnings) {
|
||||
newMigrationState.migrationWarnings = warnings;
|
||||
}
|
||||
|
||||
if (hasRequiredPrivileges !== undefined) {
|
||||
newMigrationState.hasRequiredPrivileges = hasRequiredPrivileges;
|
||||
}
|
||||
|
||||
if (migrationOp) {
|
||||
newMigrationState.status = migrationOp.status;
|
||||
|
||||
if (migrationOp.status === DataStreamMigrationStatus.notStarted) {
|
||||
return newMigrationState;
|
||||
}
|
||||
|
||||
if (migrationOp.status === DataStreamMigrationStatus.failed) {
|
||||
newMigrationState.errorMessage = migrationOp.errorMessage;
|
||||
return newMigrationState;
|
||||
}
|
||||
|
||||
if (
|
||||
migrationOp.status === DataStreamMigrationStatus.inProgress ||
|
||||
migrationOp.status === DataStreamMigrationStatus.completed
|
||||
) {
|
||||
newMigrationState.taskStatus = migrationOp.progressDetails;
|
||||
newMigrationState.taskPercComplete = migrationOp.taskPercComplete;
|
||||
}
|
||||
|
||||
if (
|
||||
migrationState.cancelLoadingState === CancelLoadingState.Requested &&
|
||||
migrationOp.status === DataStreamMigrationStatus.inProgress
|
||||
) {
|
||||
newMigrationState.cancelLoadingState = CancelLoadingState.Loading;
|
||||
}
|
||||
}
|
||||
|
||||
return newMigrationState;
|
||||
};
|
||||
|
||||
export const useMigrationStatus = ({
|
||||
dataStreamName,
|
||||
api,
|
||||
}: {
|
||||
dataStreamName: string;
|
||||
api: ApiService;
|
||||
}) => {
|
||||
const [migrationState, setMigrationState] = useState<MigrationState>({
|
||||
loadingState: LoadingState.Loading,
|
||||
errorMessage: null,
|
||||
taskPercComplete: null,
|
||||
taskStatus: undefined,
|
||||
meta: null,
|
||||
});
|
||||
|
||||
const pollIntervalIdRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const readonlyState = useRef<ReturnType<typeof readOnlyExecute> | null>(null);
|
||||
const isMounted = useRef(false);
|
||||
|
||||
const clearPollInterval = useCallback(() => {
|
||||
if (pollIntervalIdRef.current) {
|
||||
clearTimeout(pollIntervalIdRef.current);
|
||||
pollIntervalIdRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const pollingFunction = useCallback(
|
||||
async (resolutionType?: DataStreamResolutionType) => {
|
||||
clearPollInterval();
|
||||
try {
|
||||
if (resolutionType === 'readonly' && !readonlyState.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data: DataStreamReindexStatusResponse | null = null;
|
||||
let error: ResponseError | null = null;
|
||||
if (resolutionType === 'readonly') {
|
||||
if (!readonlyState.current) {
|
||||
throw new Error('Readonly state not initialized');
|
||||
}
|
||||
|
||||
const { value } = await readonlyState.current.next();
|
||||
|
||||
data = value;
|
||||
} else {
|
||||
const results = await api.getDataStreamMigrationStatus(dataStreamName);
|
||||
data = results.data;
|
||||
error = results.error;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error!.message.toString(),
|
||||
status: DataStreamMigrationStatus.fetchFailed,
|
||||
};
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return getMigrationState(prevValue, data!);
|
||||
});
|
||||
|
||||
if (data.migrationOp && data.migrationOp.status === DataStreamMigrationStatus.inProgress) {
|
||||
// Only keep polling if it exists and is in progress.
|
||||
pollIntervalIdRef.current = setTimeout(
|
||||
() => pollingFunction(migrationState.resolutionType),
|
||||
POLL_INTERVAL
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamMigrationStatus.fetchFailed,
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
[clearPollInterval, api, dataStreamName, migrationState.resolutionType]
|
||||
);
|
||||
|
||||
const updateStatus = useCallback(async () => {
|
||||
return pollingFunction(migrationState.resolutionType);
|
||||
}, [pollingFunction, migrationState.resolutionType]);
|
||||
|
||||
const startReindex = useCallback(async () => {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
status: DataStreamMigrationStatus.inProgress,
|
||||
taskPercComplete: null,
|
||||
errorMessage: null,
|
||||
cancelLoadingState: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
if (migrationState.status === DataStreamMigrationStatus.failed) {
|
||||
try {
|
||||
await api.cancelDataStreamReindexTask(dataStreamName);
|
||||
} catch (_) {
|
||||
// if the task has already failed, attempt to cancel the task
|
||||
// before attempting to start the reindexing again.
|
||||
}
|
||||
}
|
||||
|
||||
const { data: migrationOp, error } = await api.startDataStreamReindexTask(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamMigrationStatus.failed,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return getMigrationState(prevValue, { migrationOp, meta: prevValue.meta });
|
||||
});
|
||||
updateStatus();
|
||||
}, [api, dataStreamName, updateStatus, migrationState.status]);
|
||||
|
||||
const loadDataStreamMetadata = useCallback(async () => {
|
||||
try {
|
||||
const { data, error } = await api.getDataStreamMetadata(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Success,
|
||||
meta: data || null,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
// if state is completed, we don't need to update the meta
|
||||
if (prevValue.status === DataStreamMigrationStatus.completed) {
|
||||
return prevValue;
|
||||
}
|
||||
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamMigrationStatus.failed,
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [api, dataStreamName]);
|
||||
|
||||
const cancelReindex = useCallback(async () => {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
cancelLoadingState: CancelLoadingState.Requested,
|
||||
};
|
||||
});
|
||||
try {
|
||||
const { error } = await api.cancelDataStreamReindexTask(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
cancelLoadingState: CancelLoadingState.Success,
|
||||
status: DataStreamMigrationStatus.cancelled,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
cancelLoadingState: CancelLoadingState.Error,
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [api, dataStreamName]);
|
||||
|
||||
const startReadonly = useCallback(async () => {
|
||||
/**
|
||||
* Here we jsut mark the status as in progress for the polling function
|
||||
* to start executing the reindexing.
|
||||
*/
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
resolutionType: 'readonly',
|
||||
status: DataStreamMigrationStatus.inProgress,
|
||||
taskPercComplete: null,
|
||||
};
|
||||
});
|
||||
|
||||
readonlyState.current = readOnlyExecute(dataStreamName, migrationState.meta, api);
|
||||
|
||||
pollingFunction('readonly');
|
||||
}, [api, dataStreamName, migrationState, pollingFunction]);
|
||||
|
||||
const cancelReadonly = useCallback(async () => {
|
||||
readonlyState.current = null;
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
resolutionType: undefined,
|
||||
cancelLoadingState: CancelLoadingState.Success,
|
||||
status: DataStreamMigrationStatus.cancelled,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const initMigration = useCallback((resolutionType: DataStreamResolutionType) => {
|
||||
setMigrationState((prevValue: MigrationState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
resolutionType,
|
||||
status: DataStreamMigrationStatus.notStarted,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
updateStatus();
|
||||
}, [updateStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
|
||||
// Clean up on unmount.
|
||||
clearPollInterval();
|
||||
};
|
||||
}, [clearPollInterval]);
|
||||
|
||||
return {
|
||||
migrationState,
|
||||
loadDataStreamMetadata,
|
||||
initMigration,
|
||||
updateStatus,
|
||||
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
startReadonly,
|
||||
cancelReadonly,
|
||||
};
|
||||
};
|
|
@ -1,284 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useRef, useCallback, useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
DataStreamReindexStatus,
|
||||
DataStreamReindexWarning,
|
||||
DataStreamMetadata,
|
||||
DataStreamReindexStatusResponse,
|
||||
DataStreamProgressDetails,
|
||||
} from '../../../../../../common/types';
|
||||
import { CancelLoadingState, LoadingState } from '../../../types';
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
|
||||
const POLL_INTERVAL = 1000;
|
||||
|
||||
export interface ReindexState {
|
||||
loadingState: LoadingState;
|
||||
cancelLoadingState?: CancelLoadingState;
|
||||
|
||||
status?: DataStreamReindexStatus;
|
||||
reindexTaskPercComplete: number | null;
|
||||
errorMessage: string | null;
|
||||
reindexWarnings?: DataStreamReindexWarning[];
|
||||
hasRequiredPrivileges?: boolean;
|
||||
taskStatus?: DataStreamProgressDetails;
|
||||
|
||||
meta: DataStreamMetadata | null;
|
||||
}
|
||||
|
||||
const getReindexState = (
|
||||
reindexState: ReindexState,
|
||||
{
|
||||
reindexOp,
|
||||
warnings,
|
||||
hasRequiredPrivileges,
|
||||
meta: updatedMeta,
|
||||
}: DataStreamReindexStatusResponse & { meta?: DataStreamMetadata | null }
|
||||
) => {
|
||||
const newReindexState: ReindexState = {
|
||||
...reindexState,
|
||||
|
||||
reindexWarnings: warnings,
|
||||
meta: updatedMeta || reindexState.meta,
|
||||
loadingState: LoadingState.Success,
|
||||
};
|
||||
|
||||
if (warnings) {
|
||||
newReindexState.reindexWarnings = warnings;
|
||||
}
|
||||
|
||||
if (hasRequiredPrivileges !== undefined) {
|
||||
newReindexState.hasRequiredPrivileges = hasRequiredPrivileges;
|
||||
}
|
||||
|
||||
if (reindexOp) {
|
||||
newReindexState.status = reindexOp.status;
|
||||
|
||||
if (reindexOp.status === DataStreamReindexStatus.notStarted) {
|
||||
return newReindexState;
|
||||
}
|
||||
|
||||
if (reindexOp.status === DataStreamReindexStatus.failed) {
|
||||
newReindexState.errorMessage = reindexOp.errorMessage;
|
||||
return newReindexState;
|
||||
}
|
||||
|
||||
if (
|
||||
reindexOp.status === DataStreamReindexStatus.inProgress ||
|
||||
reindexOp.status === DataStreamReindexStatus.completed
|
||||
) {
|
||||
newReindexState.taskStatus = reindexOp.progressDetails;
|
||||
newReindexState.reindexTaskPercComplete = reindexOp.reindexTaskPercComplete;
|
||||
}
|
||||
|
||||
if (
|
||||
reindexState.cancelLoadingState === CancelLoadingState.Requested &&
|
||||
reindexOp.status === DataStreamReindexStatus.inProgress
|
||||
) {
|
||||
newReindexState.cancelLoadingState = CancelLoadingState.Loading;
|
||||
}
|
||||
}
|
||||
|
||||
return newReindexState;
|
||||
};
|
||||
|
||||
export const useReindexStatus = ({
|
||||
dataStreamName,
|
||||
api,
|
||||
}: {
|
||||
dataStreamName: string;
|
||||
api: ApiService;
|
||||
}) => {
|
||||
const [reindexState, setReindexState] = useState<ReindexState>({
|
||||
loadingState: LoadingState.Loading,
|
||||
errorMessage: null,
|
||||
reindexTaskPercComplete: null,
|
||||
taskStatus: undefined,
|
||||
meta: null,
|
||||
});
|
||||
|
||||
const pollIntervalIdRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const isMounted = useRef(false);
|
||||
|
||||
const clearPollInterval = useCallback(() => {
|
||||
if (pollIntervalIdRef.current) {
|
||||
clearTimeout(pollIntervalIdRef.current);
|
||||
pollIntervalIdRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateStatus = useCallback(async () => {
|
||||
clearPollInterval();
|
||||
try {
|
||||
const { data, error } = await api.getDataStreamReindexStatus(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamReindexStatus.fetchFailed,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (data === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return getReindexState(prevValue, data);
|
||||
});
|
||||
|
||||
if (data.reindexOp && data.reindexOp.status === DataStreamReindexStatus.inProgress) {
|
||||
// Only keep polling if it exists and is in progress.
|
||||
pollIntervalIdRef.current = setTimeout(updateStatus, POLL_INTERVAL);
|
||||
}
|
||||
} catch (error) {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamReindexStatus.fetchFailed,
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [clearPollInterval, api, dataStreamName]);
|
||||
|
||||
const startReindex = useCallback(async () => {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
status: DataStreamReindexStatus.inProgress,
|
||||
reindexTaskPercComplete: null,
|
||||
errorMessage: null,
|
||||
cancelLoadingState: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
if (reindexState.status === DataStreamReindexStatus.failed) {
|
||||
try {
|
||||
await api.cancelDataStreamReindexTask(dataStreamName);
|
||||
} catch (_) {
|
||||
// if the task has already failed, attempt to cancel the task
|
||||
// before attempting to start the reindexing again.
|
||||
}
|
||||
}
|
||||
|
||||
const { data: reindexOp, error } = await api.startDataStreamReindexTask(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamReindexStatus.failed,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return getReindexState(prevValue, { reindexOp, meta: prevValue.meta });
|
||||
});
|
||||
updateStatus();
|
||||
}, [api, dataStreamName, updateStatus, reindexState.status]);
|
||||
|
||||
const loadDataStreamMetadata = useCallback(async () => {
|
||||
try {
|
||||
const { data, error } = await api.getDataStreamMetadata(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Success,
|
||||
meta: data || null,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
// if state is completed, we don't need to update the meta
|
||||
if (prevValue.status === DataStreamReindexStatus.completed) {
|
||||
return prevValue;
|
||||
}
|
||||
|
||||
return {
|
||||
...prevValue,
|
||||
loadingState: LoadingState.Error,
|
||||
errorMessage: error.message.toString(),
|
||||
status: DataStreamReindexStatus.failed,
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [api, dataStreamName]);
|
||||
|
||||
const cancelReindex = useCallback(async () => {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
cancelLoadingState: CancelLoadingState.Requested,
|
||||
};
|
||||
});
|
||||
try {
|
||||
const { error } = await api.cancelDataStreamReindexTask(dataStreamName);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
cancelLoadingState: CancelLoadingState.Success,
|
||||
status: DataStreamReindexStatus.cancelled,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
setReindexState((prevValue: ReindexState) => {
|
||||
return {
|
||||
...prevValue,
|
||||
cancelLoadingState: CancelLoadingState.Error,
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [api, dataStreamName]);
|
||||
|
||||
useEffect(() => {
|
||||
updateStatus();
|
||||
}, [updateStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
|
||||
// Clean up on unmount.
|
||||
clearPollInterval();
|
||||
};
|
||||
}, [clearPollInterval]);
|
||||
|
||||
return {
|
||||
reindexState,
|
||||
loadDataStreamMetadata,
|
||||
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
updateStatus,
|
||||
};
|
||||
};
|
|
@ -8,7 +8,7 @@
|
|||
export { MlSnapshotsTableRow } from './ml_snapshots';
|
||||
export { IndexSettingsTableRow } from './index_settings';
|
||||
export { DefaultTableRow } from './default';
|
||||
export { ReindexTableRow } from './reindex';
|
||||
export { IndexTableRow } from './indices';
|
||||
export { DataStreamTableRow } from './data_streams';
|
||||
export { ClusterSettingsTableRow } from './cluster_settings';
|
||||
export { HealthIndicatorTableRow } from './health_indicator';
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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, { createContext, useContext } from 'react';
|
||||
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
import { useReindex, ReindexState } from './use_reindex';
|
||||
import { UpdateIndexState, useUpdateIndex } from './use_update_index';
|
||||
import { EnrichedDeprecationInfo, IndexAction } from '../../../../../../common/types';
|
||||
|
||||
export interface IndexStateContext {
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
reindexState: ReindexState;
|
||||
startReindex: () => Promise<void>;
|
||||
cancelReindex: () => Promise<void>;
|
||||
updateIndexState: UpdateIndexState;
|
||||
updateIndex: () => Promise<void>;
|
||||
}
|
||||
|
||||
const IndexContext = createContext<IndexStateContext | undefined>(undefined);
|
||||
|
||||
export const useIndexContext = () => {
|
||||
const context = useContext(IndexContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useIndexContext must be used within a <IndexStatusProvider />');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
api: ApiService;
|
||||
children: React.ReactNode;
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
}
|
||||
|
||||
export const IndexStatusProvider: React.FunctionComponent<Props> = ({
|
||||
api,
|
||||
deprecation,
|
||||
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({
|
||||
indexName,
|
||||
api,
|
||||
correctiveAction: deprecation.correctiveAction,
|
||||
});
|
||||
|
||||
return (
|
||||
<IndexContext.Provider
|
||||
value={{
|
||||
deprecation,
|
||||
reindexState,
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
updateIndexState,
|
||||
updateIndex,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</IndexContext.Provider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* 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, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiFlyoutHeader, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
|
||||
import { EnrichedDeprecationInfo, ReindexStatus } from '../../../../../../../common/types';
|
||||
|
||||
import type { IndexStateContext } from '../context';
|
||||
import { DeprecationBadge } from '../../../../shared';
|
||||
import {
|
||||
UIM_REINDEX_READONLY_CLICK,
|
||||
UIM_REINDEX_READONLY_RETRY_CLICK,
|
||||
UIM_REINDEX_START_CLICK,
|
||||
UIM_REINDEX_STOP_CLICK,
|
||||
UIM_REINDEX_UNFREEZE_CLICK,
|
||||
UIM_REINDEX_UNFREEZE_RETRY_CLICK,
|
||||
uiMetricService,
|
||||
} from '../../../../../lib/ui_metric';
|
||||
import {
|
||||
ReindexDetailsFlyoutStep,
|
||||
UnfreezeDetailsFlyoutStep,
|
||||
UpdateIndexFlyoutStep,
|
||||
ReindexFlyoutStep,
|
||||
WarningFlyoutStep,
|
||||
type FlyoutStep,
|
||||
} from './steps';
|
||||
|
||||
export interface IndexFlyoutProps extends IndexStateContext {
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
closeFlyout: () => void;
|
||||
}
|
||||
|
||||
export const IndexFlyout: React.FunctionComponent<IndexFlyoutProps> = ({
|
||||
reindexState,
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
updateIndexState,
|
||||
updateIndex,
|
||||
closeFlyout,
|
||||
deprecation,
|
||||
}) => {
|
||||
const { status: reindexStatus, reindexWarnings } = reindexState;
|
||||
const { status: updateIndexStatus } = updateIndexState;
|
||||
const { index, correctiveAction } = deprecation;
|
||||
|
||||
const [flyoutStep, setFlyoutStep] = useState<FlyoutStep>('details');
|
||||
|
||||
useEffect(() => {
|
||||
switch (reindexStatus) {
|
||||
case ReindexStatus.failed:
|
||||
case ReindexStatus.fetchFailed:
|
||||
case ReindexStatus.cancelled:
|
||||
case ReindexStatus.inProgress:
|
||||
case ReindexStatus.completed: {
|
||||
setFlyoutStep('reindexing');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
switch (updateIndexStatus) {
|
||||
case 'inProgress':
|
||||
case 'complete':
|
||||
case 'failed': {
|
||||
setFlyoutStep(correctiveAction?.type === 'unfreeze' ? 'unfreeze' : 'makeReadonly');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setFlyoutStep('details');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [correctiveAction?.type, reindexStatus, updateIndexStatus]);
|
||||
|
||||
const onStartReindex = useCallback(() => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_START_CLICK);
|
||||
startReindex();
|
||||
}, [startReindex]);
|
||||
|
||||
const onMakeReadonly = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_READONLY_CLICK);
|
||||
await updateIndex();
|
||||
}, [updateIndex]);
|
||||
|
||||
const onMakeReadonlyRetry = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_READONLY_RETRY_CLICK);
|
||||
await updateIndex();
|
||||
}, [updateIndex]);
|
||||
|
||||
const onUnfreeze = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_UNFREEZE_CLICK);
|
||||
await updateIndex();
|
||||
}, [updateIndex]);
|
||||
|
||||
const onUnfreezeRetry = useCallback(async () => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_UNFREEZE_RETRY_CLICK);
|
||||
await updateIndex();
|
||||
}, [updateIndex]);
|
||||
|
||||
const onStopReindex = useCallback(() => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_STOP_CLICK);
|
||||
cancelReindex();
|
||||
}, [cancelReindex]);
|
||||
|
||||
const startReindexWithWarnings = useCallback(() => {
|
||||
if (
|
||||
reindexWarnings &&
|
||||
reindexWarnings.length > 0 &&
|
||||
reindexStatus !== ReindexStatus.inProgress &&
|
||||
reindexStatus !== ReindexStatus.completed
|
||||
) {
|
||||
setFlyoutStep('confirmReindex');
|
||||
} else {
|
||||
onStartReindex();
|
||||
}
|
||||
}, [reindexWarnings, reindexStatus, onStartReindex]);
|
||||
|
||||
const flyoutContents = useMemo(() => {
|
||||
switch (flyoutStep) {
|
||||
case 'details':
|
||||
return correctiveAction?.type === 'unfreeze' ? (
|
||||
<UnfreezeDetailsFlyoutStep
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={() => {
|
||||
setFlyoutStep('confirmReindex');
|
||||
}}
|
||||
unfreeze={() => {
|
||||
setFlyoutStep('unfreeze');
|
||||
onUnfreeze();
|
||||
}}
|
||||
updateIndexState={updateIndexState}
|
||||
reindexState={reindexState}
|
||||
/>
|
||||
) : (
|
||||
<ReindexDetailsFlyoutStep
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={() => {
|
||||
setFlyoutStep('confirmReindex');
|
||||
}}
|
||||
startReadonly={() => {
|
||||
setFlyoutStep('confirmReadonly');
|
||||
}}
|
||||
deprecation={deprecation}
|
||||
updateIndexState={updateIndexState}
|
||||
reindexState={reindexState}
|
||||
/>
|
||||
);
|
||||
case 'confirmReadonly':
|
||||
case 'confirmReindex':
|
||||
const flow = flyoutStep === 'confirmReadonly' ? 'readonly' : 'reindex';
|
||||
return (
|
||||
<WarningFlyoutStep
|
||||
warnings={
|
||||
reindexState.reindexWarnings?.filter(
|
||||
({ flow: warningFlow }) => warningFlow === 'all' || warningFlow === flow
|
||||
) ?? []
|
||||
}
|
||||
meta={reindexState.meta}
|
||||
flow={flow}
|
||||
back={() => setFlyoutStep('details')}
|
||||
confirm={() => {
|
||||
if (flyoutStep === 'confirmReadonly') {
|
||||
setFlyoutStep('makeReadonly');
|
||||
onMakeReadonly();
|
||||
} else {
|
||||
onStartReindex();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 'reindexing':
|
||||
return (
|
||||
<ReindexFlyoutStep
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={startReindexWithWarnings}
|
||||
reindexState={reindexState}
|
||||
cancelReindex={onStopReindex}
|
||||
/>
|
||||
);
|
||||
case 'unfreeze':
|
||||
return (
|
||||
<UpdateIndexFlyoutStep
|
||||
action={flyoutStep}
|
||||
meta={reindexState.meta}
|
||||
retry={onUnfreezeRetry}
|
||||
updateIndexState={updateIndexState}
|
||||
closeFlyout={closeFlyout}
|
||||
/>
|
||||
);
|
||||
case 'makeReadonly':
|
||||
return (
|
||||
<UpdateIndexFlyoutStep
|
||||
action={flyoutStep}
|
||||
meta={reindexState.meta}
|
||||
retry={onMakeReadonlyRetry}
|
||||
updateIndexState={updateIndexState}
|
||||
closeFlyout={closeFlyout}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [
|
||||
flyoutStep,
|
||||
correctiveAction?.type,
|
||||
deprecation,
|
||||
closeFlyout,
|
||||
updateIndexState,
|
||||
reindexState,
|
||||
startReindexWithWarnings,
|
||||
onStopReindex,
|
||||
onUnfreezeRetry,
|
||||
onMakeReadonlyRetry,
|
||||
onUnfreeze,
|
||||
onMakeReadonly,
|
||||
onStartReindex,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<DeprecationBadge
|
||||
isCritical={deprecation.isCritical}
|
||||
isResolved={reindexStatus === ReindexStatus.completed || updateIndexStatus === 'complete'}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiTitle size="s" data-test-subj="flyoutTitle">
|
||||
<h2 id="reindexDetailsFlyoutTitle">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.flyoutHeader"
|
||||
defaultMessage="Update {index}"
|
||||
values={{ index }}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
|
||||
{flyoutContents}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,5 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export type { ReindexFlyoutProps } from './container';
|
||||
export { ReindexFlyout } from './container';
|
||||
export type { IndexFlyoutProps } from './container';
|
||||
export { IndexFlyout } from './container';
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { EuiCallOut, EuiLink, EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { EnrichedDeprecationInfo } from '../../../../../../../../../common/types';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
|
||||
interface Props {
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* We get copy directly from ES. This contains information that applies to indices
|
||||
* that are read-only or not.
|
||||
*/
|
||||
export const ESTransformsTargetGuidance = ({ deprecation }: Props) => {
|
||||
const {
|
||||
services: {
|
||||
core: { http },
|
||||
},
|
||||
} = useAppContext();
|
||||
return (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title={i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.calloutTitle',
|
||||
{ defaultMessage: 'Transforms detected' }
|
||||
)}
|
||||
data-test-subj="esTransformsGuidance"
|
||||
color="warning"
|
||||
>
|
||||
{deprecation.details}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="m">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.description1"
|
||||
defaultMessage="The reindex operation will copy all of the existing documents into a new index and remove the old one. During the reindex operation your data will be in a read-only state and transforms writing to this index will be paused."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.description2"
|
||||
defaultMessage="Depending on size and resources, reindexing may take an extended time. For indices with more than 10GB of data or to avoid transform downtime refer to the {migrationGuideLink} or {transformsLink} to manage transforms writing to this index."
|
||||
values={{
|
||||
migrationGuideLink: (
|
||||
<EuiLink target="_blank" href={deprecation.url}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.migrationGuideLink',
|
||||
{ defaultMessage: 'migration guide' }
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
transformsLink: (
|
||||
<EuiLink
|
||||
target="_blank"
|
||||
href={`${http.basePath.prepend('/app/management/data/transform')}`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.transfromsLink"
|
||||
defaultMessage="go to transforms"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { ReindexDetailsFlyoutStep } from './reindex_details_step';
|
||||
export { UnfreezeDetailsFlyoutStep } from './unfreeze_details_step';
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
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) {
|
||||
case ReindexStatus.fetchFailed:
|
||||
case ReindexStatus.failed:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.tryAgainLabel"
|
||||
defaultMessage="Try again"
|
||||
/>
|
||||
);
|
||||
case ReindexStatus.inProgress:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.reindexingLabel"
|
||||
defaultMessage="Reindexing…"
|
||||
/>
|
||||
);
|
||||
case ReindexStatus.cancelled:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.restartLabel"
|
||||
defaultMessage="Restart reindexing"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
|
||||
defaultMessage="Start reindexing"
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const getDefaultGuideanceText = ({
|
||||
isClosedIndex,
|
||||
readOnlyExcluded,
|
||||
reindexExcluded,
|
||||
indexBlockUrl,
|
||||
indexManagementUrl,
|
||||
}: {
|
||||
isClosedIndex: boolean;
|
||||
readOnlyExcluded: boolean;
|
||||
reindexExcluded: boolean;
|
||||
indexBlockUrl: string;
|
||||
indexManagementUrl: string;
|
||||
}) => {
|
||||
const guideanceListItems = [];
|
||||
if (!reindexExcluded) {
|
||||
guideanceListItems.push({
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option1.title',
|
||||
{
|
||||
defaultMessage: 'Option {optionCount}: Reindex data',
|
||||
values: { optionCount: guideanceListItems.length + 1 },
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
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>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (!readOnlyExcluded) {
|
||||
guideanceListItems.push({
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option2.title',
|
||||
{
|
||||
defaultMessage: 'Option {optionCount}: Mark as read-only',
|
||||
values: { optionCount: guideanceListItems.length + 1 },
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option2.description"
|
||||
defaultMessage="Old indices can maintain compatibility with the next major version if they are turned into read-only mode. If you no longer need to update documents in this index (or add new ones), you might want to convert it to a read-only index. {docsLink}"
|
||||
values={{
|
||||
docsLink: (
|
||||
<EuiLink target="_blank" href={indexBlockUrl}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.learnMoreLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Learn more',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
guideanceListItems.push({
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option3.title',
|
||||
{
|
||||
defaultMessage: 'Option {optionCount}: Delete index',
|
||||
values: { optionCount: guideanceListItems.length + 1 },
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option3.description"
|
||||
defaultMessage="If you no longer need it, you can also delete the index from {indexManagementLinkHtml}."
|
||||
values={{
|
||||
indexManagementLinkHtml: (
|
||||
<EuiLink href={indexManagementUrl}>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.indexMgmtLink"
|
||||
defaultMessage="Index Management"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
});
|
||||
|
||||
return guideanceListItems;
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 type { FunctionComponent } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiCallOut, EuiDescriptionList, EuiLink, EuiText } from '@elastic/eui';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
|
||||
export const MlAnomalyGuidance: FunctionComponent = () => {
|
||||
const {
|
||||
services: {
|
||||
core: { docLinks },
|
||||
},
|
||||
} = useAppContext();
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<EuiCallOut
|
||||
title={i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.notCompatibleMlAnomalyIndexTitle',
|
||||
{ defaultMessage: 'ML anomaly index detected' }
|
||||
)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.notCompatibleMlAnomalyIndexText"
|
||||
defaultMessage="Anomaly result indices that were created in 7.x must be either reindexed, marked as read-only, or deleted before upgrading to 9.x. {learnMore}."
|
||||
values={{
|
||||
learnMore: (
|
||||
<EuiLink target="_blank" href={docLinks.links.ml.anomalyMigrationGuide}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.notCompatibleMlAnomalyIndexText.learnMore',
|
||||
{ defaultMessage: 'Learn more' }
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiCallOut>
|
||||
</p>
|
||||
<EuiDescriptionList
|
||||
rowGutterSize="m"
|
||||
listItems={[
|
||||
{
|
||||
title: 'Option 1: Reindex data',
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexMlAnomalyIndexText"
|
||||
defaultMessage="While anomaly detection results are being reindexed, jobs continue to run and process new data. However, you cannot completely delete an anomaly detection job that stores results in this index until the reindexing is complete."
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Option 2: Mark as read-only',
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.readOnlyMlAnomalyIndexText"
|
||||
defaultMessage="This skips reindexing and will mark the result index as read-only. It is useful for large indices that contain the results of only one or a few anomaly detection jobs. If you delete these jobs later, you will not be able to create a new job with the same name. {learnMore} about write blocks."
|
||||
values={{
|
||||
learnMore: (
|
||||
<EuiLink target="_blank" href={docLinks.links.upgradeAssistant.indexBlocks}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.learnMoreLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Learn more',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Option 3: Delete this index',
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.deleteMlAnomalyIndexText"
|
||||
defaultMessage="Use the ML UI to delete jobs that are no longer needed. The result index is deleted when all jobs that store results in it have been deleted."
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { ReindexDetailsFlyoutStep } from './reindex_details_step';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import type { UpdateIndexState } from '../../../use_update_index';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../../../../common/types';
|
||||
|
||||
jest.mock('../../../../../../../app_context', () => {
|
||||
const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks');
|
||||
|
||||
return {
|
||||
useAppContext: () => {
|
||||
return {
|
||||
services: {
|
||||
api: {
|
||||
useLoadNodeDiskSpace: () => [],
|
||||
},
|
||||
core: {
|
||||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
http: {
|
||||
basePath: {
|
||||
prepend: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('ReindexDetailsFlyoutStep', () => {
|
||||
const defaultDeprecation: () => EnrichedDeprecationInfo = () => ({
|
||||
isCritical: true,
|
||||
message: 'foo',
|
||||
resolveDuringUpgrade: false,
|
||||
type: 'index_settings',
|
||||
url: 'https://te.st',
|
||||
});
|
||||
const defaultReindexState: () => ReindexState = () => ({
|
||||
loadingState: LoadingState.Success,
|
||||
meta: {
|
||||
indexName: 'some_index',
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
aliases: [],
|
||||
isInDataStream: false,
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
hasRequiredPrivileges: true,
|
||||
reindexTaskPercComplete: null,
|
||||
errorMessage: null,
|
||||
});
|
||||
|
||||
const defaultUpdateIndexState: () => UpdateIndexState = () => ({
|
||||
status: 'incomplete',
|
||||
failedBefore: false,
|
||||
});
|
||||
|
||||
it('renders for non-readonly indices', () => {
|
||||
const wrapper = shallow(
|
||||
<ReindexDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
startReindex={jest.fn()}
|
||||
startReadonly={jest.fn()}
|
||||
reindexState={defaultReindexState()}
|
||||
updateIndexState={defaultUpdateIndexState()}
|
||||
deprecation={defaultDeprecation()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="This index was created in ES 7.x and it is not compatible with the next major version. Choose one of the following options:"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.notCompatibleIndexText"
|
||||
/>
|
||||
</p>
|
||||
<EuiDescriptionList
|
||||
listItems={
|
||||
Array [
|
||||
Object {
|
||||
"description": <EuiText
|
||||
size="m"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
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."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option1.description"
|
||||
/>
|
||||
</EuiText>,
|
||||
"title": "Option 1: Reindex data",
|
||||
},
|
||||
Object {
|
||||
"description": <EuiText
|
||||
size="m"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Old indices can maintain compatibility with the next major version if they are turned into read-only mode. If you no longer need to update documents in this index (or add new ones), you might want to convert it to a read-only index. {docsLink}"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindex.option2.description"
|
||||
values={
|
||||
Object {
|
||||
"docsLink": <EuiLink
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/index-modules-blocks.html#index-block-settings"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>,
|
||||
"title": "Option 2: Mark as read-only",
|
||||
},
|
||||
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.reindex.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="accent"
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
disabled={false}
|
||||
fill={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Mark as read-only"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="startReindexingButton"
|
||||
disabled={false}
|
||||
fill={true}
|
||||
isLoading={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders correct guidance for indices with transforms', () => {
|
||||
const wrapper = shallow(
|
||||
<ReindexDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
startReindex={jest.fn()}
|
||||
startReadonly={jest.fn()}
|
||||
reindexState={defaultReindexState()}
|
||||
updateIndexState={defaultUpdateIndexState()}
|
||||
deprecation={{
|
||||
...defaultDeprecation(),
|
||||
correctiveAction: {
|
||||
type: 'reindex',
|
||||
transformIds: ['abc', 'def'],
|
||||
metadata: {
|
||||
isFrozenIndex: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
<ESTransformsTargetGuidance
|
||||
deprecation={
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"transformIds": Array [
|
||||
"abc",
|
||||
"def",
|
||||
],
|
||||
"type": "reindex",
|
||||
},
|
||||
"isCritical": true,
|
||||
"message": "foo",
|
||||
"resolveDuringUpgrade": false,
|
||||
"type": "index_settings",
|
||||
"url": "https://te.st",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</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}
|
||||
fill={true}
|
||||
isLoading={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders for readonly indices (warning deprecation)', () => {
|
||||
const props = defaultReindexState();
|
||||
props.meta.isReadonly = true;
|
||||
|
||||
const wrapper = shallow(
|
||||
<ReindexDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
startReindex={jest.fn()}
|
||||
startReadonly={jest.fn()}
|
||||
reindexState={props}
|
||||
updateIndexState={defaultUpdateIndexState()}
|
||||
deprecation={defaultDeprecation()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="This index was created in ES 7.x. It has been marked as read-only, which enables compatibility with the next major version."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.readonlyCompatibleIndexText"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
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."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexText"
|
||||
/>
|
||||
</p>
|
||||
</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}
|
||||
fill={true}
|
||||
isLoading={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders ML anomaly index guidance', () => {
|
||||
const reindexState = defaultReindexState();
|
||||
reindexState.meta.indexName = '.ml-anomalies-1';
|
||||
const deprecation = defaultDeprecation();
|
||||
deprecation.index = '.ml-anomalies-1';
|
||||
const wrapper = shallow(
|
||||
<ReindexDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
startReindex={jest.fn()}
|
||||
startReadonly={jest.fn()}
|
||||
reindexState={reindexState}
|
||||
updateIndexState={defaultUpdateIndexState()}
|
||||
deprecation={deprecation}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
<MlAnomalyGuidance />
|
||||
</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="accent"
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
disabled={false}
|
||||
fill={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Mark as read-only"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="startReindexingButton"
|
||||
disabled={false}
|
||||
fill={true}
|
||||
isLoading={false}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiDescriptionList,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import {
|
||||
EnrichedDeprecationInfo,
|
||||
ReindexAction,
|
||||
ReindexStatus,
|
||||
} from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
import { getDefaultGuideanceText, getReindexButtonLabel } from './messages';
|
||||
import { FrozenCallOut } from '../frozen_callout';
|
||||
import type { UpdateIndexState } from '../../../use_update_index';
|
||||
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-';
|
||||
|
||||
/**
|
||||
* Displays a flyout that shows the details / corrective action for a "reindex" deprecation for a given index.
|
||||
*/
|
||||
export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
|
||||
reindexState: ReindexState;
|
||||
updateIndexState: UpdateIndexState;
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
startReindex: () => void;
|
||||
startReadonly: () => void;
|
||||
closeFlyout: () => void;
|
||||
}> = ({
|
||||
reindexState,
|
||||
updateIndexState,
|
||||
deprecation,
|
||||
startReindex,
|
||||
startReadonly,
|
||||
closeFlyout,
|
||||
}) => {
|
||||
const {
|
||||
services: {
|
||||
api,
|
||||
core: { docLinks, http },
|
||||
},
|
||||
} = useAppContext();
|
||||
|
||||
const { loadingState, status: reindexStatus, hasRequiredPrivileges, meta } = reindexState;
|
||||
const { status: updateIndexStatus } = updateIndexState;
|
||||
const { indexName, isFrozen, isClosedIndex, isReadonly } = meta;
|
||||
const loading = loadingState === LoadingState.Loading;
|
||||
const isCompleted = reindexStatus === ReindexStatus.completed || updateIndexStatus === 'complete';
|
||||
const hasFetchFailed = reindexStatus === ReindexStatus.fetchFailed;
|
||||
const hasReindexingFailed = reindexStatus === ReindexStatus.failed;
|
||||
const correctiveAction = deprecation.correctiveAction as ReindexAction | undefined;
|
||||
const isESTransformTarget = !!correctiveAction?.transformIds?.length;
|
||||
const isMLAnomalyIndex = Boolean(indexName?.startsWith(ML_ANOMALIES_PREFIX));
|
||||
const { excludedActions = [] } = (deprecation.correctiveAction as ReindexAction) || {};
|
||||
const readOnlyExcluded = excludedActions.includes('readOnly');
|
||||
const reindexExcluded = excludedActions.includes('reindex');
|
||||
|
||||
const { data: nodes } = api.useLoadNodeDiskSpace();
|
||||
|
||||
let showEsTransformsGuidance = false;
|
||||
let showMlAnomalyReindexingGuidance = false;
|
||||
let showReadOnlyGuidance = false;
|
||||
let showDefaultGuidance = false;
|
||||
|
||||
if (isESTransformTarget) {
|
||||
showEsTransformsGuidance = true;
|
||||
} else if (isReadonly) {
|
||||
showReadOnlyGuidance = true;
|
||||
} else if (isMLAnomalyIndex) {
|
||||
showMlAnomalyReindexingGuidance = true;
|
||||
} else {
|
||||
showDefaultGuidance = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
{hasRequiredPrivileges === false && (
|
||||
<Fragment>
|
||||
<EuiSpacer />
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to reindex this index."
|
||||
/>
|
||||
}
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{nodes && nodes.length > 0 && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
data-test-subj="lowDiskSpaceCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.lowDiskSpaceCalloutTitle"
|
||||
defaultMessage="Nodes with low disk space"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent reindexing. The following nodes are impacted:"
|
||||
/>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<ul>
|
||||
{nodes.map(({ nodeName, available, nodeId }) => (
|
||||
<li key={nodeId} data-test-subj="impactedNodeListItem">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.lowDiskSpaceUsedText"
|
||||
defaultMessage="{nodeName} ({available} available)"
|
||||
values={{
|
||||
nodeName,
|
||||
available,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasFetchFailed && <FetchFailedCallOut errorMessage={reindexState.errorMessage!} />}
|
||||
|
||||
{!hasFetchFailed && hasReindexingFailed && (
|
||||
<ReindexingFailedCallOut errorMessage={reindexState.errorMessage!} />
|
||||
)}
|
||||
|
||||
{isFrozen && <FrozenCallOut />}
|
||||
|
||||
<EuiText>
|
||||
{showEsTransformsGuidance && <ESTransformsTargetGuidance deprecation={deprecation} />}
|
||||
{showMlAnomalyReindexingGuidance && <MlAnomalyGuidance />}
|
||||
{showReadOnlyGuidance && (
|
||||
<Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.readonlyCompatibleIndexText"
|
||||
defaultMessage="This index was created in ES 7.x. It has been marked as read-only, which enables compatibility with the next major version."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexText"
|
||||
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 && (
|
||||
<Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.notCompatibleIndexText"
|
||||
defaultMessage="This index was created in ES 7.x and it is not compatible with the next major version. Choose one of the following options:"
|
||||
/>
|
||||
</p>
|
||||
<EuiDescriptionList
|
||||
rowGutterSize="m"
|
||||
listItems={getDefaultGuideanceText({
|
||||
isClosedIndex,
|
||||
readOnlyExcluded,
|
||||
reindexExcluded,
|
||||
indexManagementUrl: `${http.basePath.prepend(
|
||||
`/app/management/data/index_management/indices/index_details?indexName=${indexName}`
|
||||
)}`,
|
||||
indexBlockUrl: docLinks.links.upgradeAssistant.indexBlocks,
|
||||
})}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
{!isReadonly &&
|
||||
!hasFetchFailed &&
|
||||
!isCompleted &&
|
||||
hasRequiredPrivileges &&
|
||||
!isESTransformTarget &&
|
||||
!readOnlyExcluded && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
onClick={startReadonly}
|
||||
disabled={loading}
|
||||
color={reindexExcluded ? 'primary' : 'accent'}
|
||||
fill={reindexExcluded}
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton"
|
||||
defaultMessage="Mark as read-only"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{!hasFetchFailed && !isCompleted && hasRequiredPrivileges && !reindexExcluded && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
color={reindexStatus === ReindexStatus.cancelled ? 'warning' : 'primary'}
|
||||
iconType={reindexStatus === ReindexStatus.cancelled ? 'play' : undefined}
|
||||
onClick={startReindex}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
data-test-subj="startReindexingButton"
|
||||
>
|
||||
{getReindexButtonLabel(reindexStatus)}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import type { UpdateIndexState } from '../../../use_update_index';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import { UnfreezeDetailsFlyoutStep } from './unfreeze_details_step';
|
||||
|
||||
jest.mock('../../../../../../../app_context', () => {
|
||||
const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks');
|
||||
|
||||
return {
|
||||
useAppContext: () => {
|
||||
return {
|
||||
services: {
|
||||
api: {
|
||||
useLoadNodeDiskSpace: () => [],
|
||||
},
|
||||
core: {
|
||||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
http: {
|
||||
basePath: {
|
||||
prepend: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('UnfreezeDetailsFlyoutStep', () => {
|
||||
const defaultReindexState: ReindexState = {
|
||||
loadingState: LoadingState.Success,
|
||||
meta: {
|
||||
indexName: 'some_index',
|
||||
aliases: [],
|
||||
isFrozen: true,
|
||||
isReadonly: true,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
},
|
||||
hasRequiredPrivileges: true,
|
||||
reindexTaskPercComplete: null,
|
||||
errorMessage: null,
|
||||
};
|
||||
|
||||
const defaultUpdateIndexState: UpdateIndexState = {
|
||||
status: 'incomplete',
|
||||
failedBefore: false,
|
||||
};
|
||||
|
||||
it('renders all options for regular indices', () => {
|
||||
const wrapper = shallow(
|
||||
<UnfreezeDetailsFlyoutStep
|
||||
closeFlyout={jest.fn()}
|
||||
startReindex={jest.fn()}
|
||||
unfreeze={jest.fn()}
|
||||
reindexState={defaultReindexState}
|
||||
updateIndexState={defaultUpdateIndexState}
|
||||
/>
|
||||
);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiDescriptionList,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { ReindexStatus } from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
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.
|
||||
*/
|
||||
export const UnfreezeDetailsFlyoutStep: React.FunctionComponent<{
|
||||
closeFlyout: () => void;
|
||||
reindexState: ReindexState;
|
||||
updateIndexState: UpdateIndexState;
|
||||
startReindex: () => void;
|
||||
unfreeze: () => void;
|
||||
}> = ({ closeFlyout, reindexState, updateIndexState, startReindex, unfreeze }) => {
|
||||
const {
|
||||
services: {
|
||||
api,
|
||||
core: { http },
|
||||
},
|
||||
} = useAppContext();
|
||||
|
||||
const { loadingState, status: reindexStatus, hasRequiredPrivileges, meta } = reindexState;
|
||||
const { status: updateIndexStatus } = updateIndexState;
|
||||
const { indexName, isInDataStream, isClosedIndex } = meta;
|
||||
const loading = loadingState === LoadingState.Loading;
|
||||
const isCompleted = reindexStatus === ReindexStatus.completed || updateIndexStatus === 'complete';
|
||||
const hasFetchFailed = reindexStatus === ReindexStatus.fetchFailed;
|
||||
const hasReindexingFailed = reindexStatus === ReindexStatus.failed;
|
||||
|
||||
const { data: nodes } = api.useLoadNodeDiskSpace();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
{hasRequiredPrivileges === false && (
|
||||
<Fragment>
|
||||
<EuiSpacer />
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to reindex this index."
|
||||
/>
|
||||
}
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{nodes && nodes.length > 0 && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
data-test-subj="lowDiskSpaceCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.lowDiskSpaceCalloutTitle"
|
||||
defaultMessage="Nodes with low disk space"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent reindexing. The following nodes are impacted:"
|
||||
/>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<ul>
|
||||
{nodes.map(({ nodeName, available, nodeId }) => (
|
||||
<li key={nodeId} data-test-subj="impactedNodeListItem">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.lowDiskSpaceUsedText"
|
||||
defaultMessage="{nodeName} ({available} available)"
|
||||
values={{
|
||||
nodeName,
|
||||
available,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasFetchFailed && <FetchFailedCallOut errorMessage={reindexState.errorMessage!} />}
|
||||
|
||||
{!hasFetchFailed && hasReindexingFailed && (
|
||||
<ReindexingFailedCallOut errorMessage={reindexState.errorMessage!} />
|
||||
)}
|
||||
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.frozenIndexText"
|
||||
defaultMessage="This index is frozen. Frozen indices will no longer be supported after the upgrade. Choose one of the following options:"
|
||||
/>
|
||||
</p>
|
||||
<EuiDescriptionList
|
||||
rowGutterSize="m"
|
||||
listItems={[
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.title',
|
||||
{
|
||||
defaultMessage: 'Option 1: Unfreeze index',
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreeze.option1.description"
|
||||
defaultMessage="Unfreeze this index and make it read-only. This ensures that the index will remain compatible with the next major version."
|
||||
/>
|
||||
</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.alternativeOption.title',
|
||||
{
|
||||
defaultMessage: 'Alternative: Delete the index',
|
||||
}
|
||||
),
|
||||
description: (
|
||||
<EuiText size="m">
|
||||
<FormattedMessage
|
||||
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: (
|
||||
<EuiLink
|
||||
href={`${http.basePath.prepend(
|
||||
`/app/management/data/index_management/indices/index_details?indexName=${indexName}`
|
||||
)}`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.indexMgmtLink"
|
||||
defaultMessage="Index Management"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
{/* 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'}
|
||||
iconType={reindexStatus === ReindexStatus.cancelled ? 'play' : undefined}
|
||||
onClick={startReindex}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
data-test-subj="startReindexingButton"
|
||||
>
|
||||
{getReindexButtonLabel(reindexStatus)}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{!hasFetchFailed && !isCompleted && hasRequiredPrivileges && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
onClick={unfreeze}
|
||||
disabled={loading}
|
||||
data-test-subj="startUnfreezeButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.unfreezeIndexButton"
|
||||
defaultMessage="Unfreeze"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
interface Props {
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export const FetchFailedCallOut: React.FunctionComponent<Props> = (props) => {
|
||||
const { errorMessage } = props;
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiCallOut
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj="fetchFailedCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.fetchFailedCalloutTitle"
|
||||
defaultMessage="Reindex status not available"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{errorMessage}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useAppContext } from '../../../../../../app_context';
|
||||
|
||||
export const FrozenCallOut: React.FunctionComponent = () => {
|
||||
const {
|
||||
services: {
|
||||
core: { docLinks },
|
||||
},
|
||||
} = useAppContext();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.frozenCallout.reindexFrozenIndexTitle"
|
||||
defaultMessage="This index is frozen"
|
||||
/>
|
||||
}
|
||||
iconType="iInCircle"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.frozenCallout.reindexFrozenIndex"
|
||||
defaultMessage="Frozen indices will no longer be supported after the upgrade. As a result, this index will be transformed into a non-frozen index during the update operation. {docsLink}"
|
||||
values={{
|
||||
docsLink: (
|
||||
<EuiLink target="_blank" href={docLinks.links.upgradeAssistant.unfreezeApi}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.learnMoreLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Learn more',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type { FlyoutStep } from './types';
|
||||
export { ReindexDetailsFlyoutStep } from './details/reindex_details_step';
|
||||
export { UnfreezeDetailsFlyoutStep } from './details/unfreeze_details_step';
|
||||
export { WarningFlyoutStep } from './warning/warning_step';
|
||||
export { ReindexFlyoutStep } from './reindex/reindex_step';
|
||||
export { UpdateIndexFlyoutStep } from './update/update_step';
|
|
@ -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>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,13 +1,13 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ChecklistFlyout renders 1`] = `
|
||||
exports[`ReindexStep renders 1`] = `
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="The index will be read-only during reindexing. You won't be able to add, update, or delete documents until reindexing is complete. If you need to reindex to a new cluster, use the reindex API. {docsLink}"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexDescription"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexDescription"
|
||||
values={
|
||||
Object {
|
||||
"docsLink": <EuiLink
|
||||
|
@ -23,7 +23,7 @@ exports[`ChecklistFlyout renders 1`] = `
|
|||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Reindexing is performed in the background. You can return to the Upgrade Assistant to view progress or resume reindexing after a Kibana restart."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.readonlyCallout.backgroundResumeDetail"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.readonlyCallout.backgroundResumeDetail"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
|
@ -39,6 +39,10 @@ exports[`ChecklistFlyout renders 1`] = `
|
|||
"meta": Object {
|
||||
"aliases": Array [],
|
||||
"indexName": "myIndex",
|
||||
"isClosedIndex": false,
|
||||
"isFrozen": false,
|
||||
"isInDataStream": false,
|
||||
"isReadonly": false,
|
||||
"reindexName": "reindexed-myIndex",
|
||||
},
|
||||
"reindexTaskPercComplete": null,
|
||||
|
@ -62,7 +66,7 @@ exports[`ChecklistFlyout renders 1`] = `
|
|||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.closeButtonLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
|
@ -79,7 +83,7 @@ exports[`ChecklistFlyout renders 1`] = `
|
|||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.runReindexLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
@ -88,14 +92,15 @@ exports[`ChecklistFlyout renders 1`] = `
|
|||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`ChecklistFlyout renders for frozen indices 1`] = `
|
||||
exports[`ReindexStep renders for frozen indices 1`] = `
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<FrozenCallOut />
|
||||
<EuiText>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="The index will be read-only during reindexing. You won't be able to add, update, or delete documents until reindexing is complete. If you need to reindex to a new cluster, use the reindex API. {docsLink}"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexDescription"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexDescription"
|
||||
values={
|
||||
Object {
|
||||
"docsLink": <EuiLink
|
||||
|
@ -108,35 +113,10 @@ exports[`ChecklistFlyout renders for frozen indices 1`] = `
|
|||
}
|
||||
/>
|
||||
</p>
|
||||
<EuiCallOut
|
||||
iconType="iInCircle"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="This index is frozen"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexFrozenIndexTitle"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Frozen indices will no longer be supported after upgrade, so this index will be deleted as part the reindex operation. {docsLink}"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexFrozenIndex"
|
||||
values={
|
||||
Object {
|
||||
"docsLink": <EuiLink
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/unfreeze-index-api.html"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Reindexing is performed in the background. You can return to the Upgrade Assistant to view progress or resume reindexing after a Kibana restart."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.readonlyCallout.backgroundResumeDetail"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.readonlyCallout.backgroundResumeDetail"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
|
@ -152,6 +132,10 @@ exports[`ChecklistFlyout renders for frozen indices 1`] = `
|
|||
"meta": Object {
|
||||
"aliases": Array [],
|
||||
"indexName": "myIndex",
|
||||
"isClosedIndex": false,
|
||||
"isFrozen": true,
|
||||
"isInDataStream": false,
|
||||
"isReadonly": false,
|
||||
"reindexName": "reindexed-myIndex",
|
||||
},
|
||||
"reindexTaskPercComplete": null,
|
||||
|
@ -175,7 +159,7 @@ exports[`ChecklistFlyout renders for frozen indices 1`] = `
|
|||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.closeButtonLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
|
@ -192,7 +176,7 @@ exports[`ChecklistFlyout renders for frozen indices 1`] = `
|
|||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Start reindexing"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.runReindexLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.runReindexLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
|
@ -8,9 +8,9 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../types';
|
||||
import type { ReindexState } from '../use_reindex_state';
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import { ReindexProgress } from './progress';
|
||||
|
||||
describe('ReindexProgress', () => {
|
||||
|
@ -28,6 +28,10 @@ describe('ReindexProgress', () => {
|
|||
indexName: 'foo',
|
||||
reindexName: 'reindexed-foo',
|
||||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
} as ReindexState
|
||||
}
|
||||
|
@ -44,7 +48,7 @@ describe('ReindexProgress', () => {
|
|||
<h3>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Reindexing in progress… {percents}"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingInProgressTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingInProgressTitle"
|
||||
values={
|
||||
Object {
|
||||
"percents": "0%",
|
||||
|
@ -60,7 +64,7 @@ describe('ReindexProgress', () => {
|
|||
"status": "inProgress",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Setting {indexName} index to read-only."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.readonlyStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.readonlyStepTitle"
|
||||
values={
|
||||
Object {
|
||||
"indexName": <EuiCode>
|
||||
|
@ -74,7 +78,7 @@ describe('ReindexProgress', () => {
|
|||
"status": "incomplete",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Create {reindexName} index."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.createIndexStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.createIndexStepTitle"
|
||||
values={
|
||||
Object {
|
||||
"reindexName": <EuiCode>
|
||||
|
@ -96,6 +100,10 @@ describe('ReindexProgress', () => {
|
|||
"meta": Object {
|
||||
"aliases": Array [],
|
||||
"indexName": "foo",
|
||||
"isClosedIndex": false,
|
||||
"isFrozen": false,
|
||||
"isInDataStream": false,
|
||||
"isReadonly": false,
|
||||
"reindexName": "reindexed-foo",
|
||||
},
|
||||
"reindexTaskPercComplete": null,
|
||||
|
@ -108,7 +116,7 @@ describe('ReindexProgress', () => {
|
|||
"status": "incomplete",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Copy original index settings from {indexName} to {reindexName}."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.indexSettingsRestoredStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.indexSettingsRestoredStepTitle"
|
||||
values={
|
||||
Object {
|
||||
"indexName": <EuiCode>
|
||||
|
@ -125,7 +133,7 @@ describe('ReindexProgress', () => {
|
|||
"status": "incomplete",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Create {indexName} alias for {reindexName} index."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.aliasCreatedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.aliasCreatedStepTitle"
|
||||
values={
|
||||
Object {
|
||||
"indexName": <EuiCode>
|
||||
|
@ -142,7 +150,7 @@ describe('ReindexProgress', () => {
|
|||
"status": "incomplete",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Delete original {indexName} index."
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.originalIndexDeletedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.originalIndexDeletedStepTitle"
|
||||
values={
|
||||
Object {
|
||||
"indexName": <EuiCode>
|
||||
|
@ -173,6 +181,10 @@ describe('ReindexProgress', () => {
|
|||
indexName: 'foo',
|
||||
reindexName: 'reindexed-foo',
|
||||
aliases: [],
|
||||
isFrozen: true,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
} as ReindexState
|
||||
}
|
|
@ -18,11 +18,11 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../common/types';
|
||||
import { CancelLoadingState } from '../../../../types';
|
||||
import type { ReindexState } from '../use_reindex_state';
|
||||
import { StepProgress, StepProgressStep } from './step_progress';
|
||||
import { getReindexProgressLabel } from '../../../../../lib/utils';
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../../../common/types';
|
||||
import { CancelLoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import { StepProgress, StepProgressStep } from '../../../../../common/step_progress';
|
||||
import { getReindexProgressLabel } from '../../../../../../../lib/utils';
|
||||
|
||||
const ErrorCallout: React.FunctionComponent<{ errorMessage: string | null }> = ({
|
||||
errorMessage,
|
||||
|
@ -49,7 +49,7 @@ const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
return (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.cancelledTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.cancelledTitle"
|
||||
defaultMessage="Reindexing cancelled."
|
||||
/>
|
||||
</>
|
||||
|
@ -71,7 +71,7 @@ const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
case CancelLoadingState.Loading:
|
||||
cancelText = (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.cancellingLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.cancelButton.cancellingLabel"
|
||||
defaultMessage="Cancelling…"
|
||||
/>
|
||||
);
|
||||
|
@ -79,7 +79,7 @@ const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
case CancelLoadingState.Success:
|
||||
cancelText = (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.cancelledLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.cancelButton.cancelledLabel"
|
||||
defaultMessage="Cancelled"
|
||||
/>
|
||||
);
|
||||
|
@ -87,7 +87,7 @@ const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
case CancelLoadingState.Error:
|
||||
cancelText = (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.errorLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.cancelButton.errorLabel"
|
||||
defaultMessage="Could not cancel"
|
||||
/>
|
||||
);
|
||||
|
@ -95,7 +95,7 @@ const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
default:
|
||||
cancelText = (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.cancelButton.cancelLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.cancelButton.cancelLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
);
|
||||
|
@ -106,12 +106,12 @@ const ReindexingDocumentsStepTitle: React.FunctionComponent<{
|
|||
<EuiFlexItem grow={false}>
|
||||
{stepInProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.reindexingDocumentsStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindexing documents."
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.reindexingDocumentsStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.reindexingDocumentsStepTitle"
|
||||
defaultMessage="Reindex documents."
|
||||
/>
|
||||
)}
|
||||
|
@ -141,7 +141,7 @@ const getStepTitle = (
|
|||
if (step === ReindexStep.readonly) {
|
||||
return inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.readonlyStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.readonlyStepTitle"
|
||||
defaultMessage="Setting {indexName} index to read-only."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -149,7 +149,7 @@ const getStepTitle = (
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.readonlyStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.readonlyStepTitle"
|
||||
defaultMessage="Set {indexName} index to read-only."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -161,7 +161,7 @@ const getStepTitle = (
|
|||
if (step === ReindexStep.newIndexCreated) {
|
||||
return inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.createIndexStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.createIndexStepTitle"
|
||||
defaultMessage="Creating {reindexName} index."
|
||||
values={{
|
||||
reindexName: <EuiCode>{meta.reindexName}</EuiCode>,
|
||||
|
@ -169,7 +169,7 @@ const getStepTitle = (
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.createIndexStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.createIndexStepTitle"
|
||||
defaultMessage="Create {reindexName} index."
|
||||
values={{
|
||||
reindexName: <EuiCode>{meta.reindexName}</EuiCode>,
|
||||
|
@ -181,7 +181,7 @@ const getStepTitle = (
|
|||
if (step === ReindexStep.indexSettingsRestored) {
|
||||
return inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.indexSettingsRestoredStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.indexSettingsRestoredStepTitle"
|
||||
defaultMessage="Copying original index settings from {indexName} to {reindexName}."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -190,7 +190,7 @@ const getStepTitle = (
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.indexSettingsRestoredStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.indexSettingsRestoredStepTitle"
|
||||
defaultMessage="Copy original index settings from {indexName} to {reindexName}."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -203,7 +203,7 @@ const getStepTitle = (
|
|||
if (step === ReindexStep.aliasCreated) {
|
||||
return inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.aliasCreatedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.aliasCreatedStepTitle"
|
||||
defaultMessage="Creating {indexName} alias for {reindexName} index."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -212,7 +212,7 @@ const getStepTitle = (
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.aliasCreatedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.aliasCreatedStepTitle"
|
||||
defaultMessage="Create {indexName} alias for {reindexName} index."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -225,7 +225,7 @@ const getStepTitle = (
|
|||
if (step === ReindexStep.originalIndexDeleted) {
|
||||
return inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.originalIndexDeletedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.originalIndexDeletedStepTitle"
|
||||
defaultMessage="Deleting original {indexName} index."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -233,7 +233,7 @@ const getStepTitle = (
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.originalIndexDeletedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.originalIndexDeletedStepTitle"
|
||||
defaultMessage="Delete original {indexName} index."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta.indexName}</EuiCode>,
|
||||
|
@ -245,7 +245,7 @@ const getStepTitle = (
|
|||
if (step === ReindexStep.existingAliasesUpdated) {
|
||||
return inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.inProgress.aliasesUpdatedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.inProgress.aliasesUpdatedStepTitle"
|
||||
defaultMessage="Updating {existingAliases} aliases to point to {reindexName} index."
|
||||
values={{
|
||||
existingAliases: <EuiCode>{`[${meta.aliases.join(',')}]`}</EuiCode>,
|
||||
|
@ -254,7 +254,7 @@ const getStepTitle = (
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklist.aliasesUpdatedStepTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklist.aliasesUpdatedStepTitle"
|
||||
defaultMessage="Update {existingAliases} aliases to point to {reindexName} index."
|
||||
values={{
|
||||
existingAliases: <EuiCode>{`[${meta.aliases.join(',')}]`}</EuiCode>,
|
||||
|
@ -378,7 +378,7 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
<h3>
|
||||
{status === ReindexStatus.inProgress ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingInProgressTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingInProgressTitle"
|
||||
defaultMessage="Reindexing in progress… {percents}"
|
||||
values={{
|
||||
percents: getReindexProgressLabel(
|
||||
|
@ -391,7 +391,7 @@ export const ReindexProgress: React.FunctionComponent<Props> = (props) => {
|
|||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingChecklistTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingChecklistTitle"
|
||||
defaultMessage="Reindexing process"
|
||||
/>
|
||||
)}
|
|
@ -9,12 +9,12 @@ import { shallow } from 'enzyme';
|
|||
import { cloneDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { ReindexStatus } from '../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../types';
|
||||
import type { ReindexState } from '../use_reindex_state';
|
||||
import { ChecklistFlyoutStep } from './checklist_step';
|
||||
import { ReindexStatus } from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import { ReindexFlyoutStep } from './reindex_step';
|
||||
|
||||
jest.mock('../../../../../app_context', () => {
|
||||
jest.mock('../../../../../../../app_context', () => {
|
||||
const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks');
|
||||
|
||||
return {
|
||||
|
@ -33,10 +33,9 @@ jest.mock('../../../../../app_context', () => {
|
|||
};
|
||||
});
|
||||
|
||||
describe('ChecklistFlyout', () => {
|
||||
describe('ReindexStep', () => {
|
||||
const defaultProps = {
|
||||
indexName: 'myIndex',
|
||||
frozen: false,
|
||||
closeFlyout: jest.fn(),
|
||||
confirmInputValue: 'CONFIRM',
|
||||
onConfirmInputChange: jest.fn(),
|
||||
|
@ -60,31 +59,35 @@ describe('ChecklistFlyout', () => {
|
|||
indexName: 'myIndex',
|
||||
reindexName: 'reindexed-myIndex',
|
||||
aliases: [],
|
||||
isReadonly: false,
|
||||
isFrozen: false,
|
||||
isInDataStream: false,
|
||||
isClosedIndex: false,
|
||||
},
|
||||
} as ReindexState,
|
||||
};
|
||||
|
||||
it('renders', () => {
|
||||
expect(shallow(<ChecklistFlyoutStep {...defaultProps} />)).toMatchSnapshot();
|
||||
expect(shallow(<ReindexFlyoutStep {...defaultProps} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders for frozen indices', () => {
|
||||
const props = cloneDeep(defaultProps);
|
||||
props.frozen = true;
|
||||
expect(shallow(<ChecklistFlyoutStep {...props} />)).toMatchSnapshot();
|
||||
props.reindexState.meta.isFrozen = true;
|
||||
expect(shallow(<ReindexFlyoutStep {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('disables button while reindexing', () => {
|
||||
const props = cloneDeep(defaultProps);
|
||||
props.reindexState.status = ReindexStatus.inProgress;
|
||||
const wrapper = shallow(<ChecklistFlyoutStep {...props} />);
|
||||
const wrapper = shallow(<ReindexFlyoutStep {...props} />);
|
||||
expect((wrapper.find('EuiButton').props() as any).isLoading).toBe(true);
|
||||
});
|
||||
|
||||
it('hides button if hasRequiredPrivileges is false', () => {
|
||||
const props = cloneDeep(defaultProps);
|
||||
props.reindexState.hasRequiredPrivileges = false;
|
||||
const wrapper = shallow(<ChecklistFlyoutStep {...props} />);
|
||||
const wrapper = shallow(<ReindexFlyoutStep {...props} />);
|
||||
expect(wrapper.exists('EuiButton')).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -92,24 +95,30 @@ describe('ChecklistFlyout', () => {
|
|||
const props = cloneDeep(defaultProps);
|
||||
props.reindexState.status = ReindexStatus.fetchFailed;
|
||||
props.reindexState.errorMessage = 'Index not found';
|
||||
const wrapper = shallow(<ChecklistFlyoutStep {...props} />);
|
||||
const wrapper = shallow(<ReindexFlyoutStep {...props} />);
|
||||
expect(wrapper.exists('EuiButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('shows get status error callout', () => {
|
||||
it('shows fetch failed error callout', () => {
|
||||
const props = cloneDeep(defaultProps);
|
||||
props.reindexState.status = ReindexStatus.fetchFailed;
|
||||
props.reindexState.errorMessage = 'Index not found';
|
||||
const wrapper = shallow(<ChecklistFlyoutStep {...props} />);
|
||||
expect(wrapper.exists('[data-test-subj="fetchFailedCallout"]')).toBe(true);
|
||||
const wrapper = shallow(<ReindexFlyoutStep {...props} />);
|
||||
expect(wrapper.find('FetchFailedCallOut').exists()).toBe(true);
|
||||
expect(wrapper.find('FetchFailedCallOut').props()).toEqual({
|
||||
errorMessage: 'Index not found',
|
||||
});
|
||||
});
|
||||
|
||||
it('shows reindexing callout', () => {
|
||||
const props = cloneDeep(defaultProps);
|
||||
props.reindexState.status = ReindexStatus.failed;
|
||||
props.reindexState.errorMessage = 'Index not found';
|
||||
const wrapper = shallow(<ChecklistFlyoutStep {...props} />);
|
||||
expect(wrapper.exists('[data-test-subj="reindexingFailedCallout"]')).toBe(true);
|
||||
props.reindexState.errorMessage = 'Reindex failed';
|
||||
const wrapper = shallow(<ReindexFlyoutStep {...props} />);
|
||||
expect(wrapper.find('ReindexingFailedCallOut').exists()).toBe(true);
|
||||
expect(wrapper.find('ReindexingFailedCallOut').props()).toEqual({
|
||||
errorMessage: 'Reindex failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls startReindex when button is clicked', () => {
|
||||
|
@ -121,7 +130,7 @@ describe('ChecklistFlyout', () => {
|
|||
status: undefined,
|
||||
},
|
||||
};
|
||||
const wrapper = shallow(<ChecklistFlyoutStep {...props} />);
|
||||
const wrapper = shallow(<ReindexFlyoutStep {...props} />);
|
||||
|
||||
wrapper.find('EuiButton').simulate('click');
|
||||
expect(props.startReindex).toHaveBeenCalled();
|
|
@ -22,46 +22,49 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ReindexStatus } from '../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../types';
|
||||
import type { ReindexState } from '../use_reindex_state';
|
||||
import { ReindexStatus } from '../../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../../types';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import { ReindexProgress } from './progress';
|
||||
import { useAppContext } from '../../../../../app_context';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
import { FrozenCallOut } from '../frozen_callout';
|
||||
import { FetchFailedCallOut } from '../fetch_failed_callout';
|
||||
import { ReindexingFailedCallOut } from '../reindexing_failed_callout';
|
||||
|
||||
const buttonLabel = (status?: ReindexStatus) => {
|
||||
switch (status) {
|
||||
case ReindexStatus.failed:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.tryAgainLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.tryAgainLabel"
|
||||
defaultMessage="Try again"
|
||||
/>
|
||||
);
|
||||
case ReindexStatus.inProgress:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.reindexingLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.reindexingLabel"
|
||||
defaultMessage="Reindexing…"
|
||||
/>
|
||||
);
|
||||
case ReindexStatus.paused:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.resumeLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.resumeLabel"
|
||||
defaultMessage="Resume reindexing"
|
||||
/>
|
||||
);
|
||||
case ReindexStatus.cancelled:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.restartLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.restartLabel"
|
||||
defaultMessage="Restart reindexing"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexButton.runReindexLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexButton.runReindexLabel"
|
||||
defaultMessage="Start reindexing"
|
||||
/>
|
||||
);
|
||||
|
@ -71,13 +74,12 @@ const buttonLabel = (status?: ReindexStatus) => {
|
|||
/**
|
||||
* Displays a flyout that shows the current reindexing status for a given index.
|
||||
*/
|
||||
export const ChecklistFlyoutStep: React.FunctionComponent<{
|
||||
frozen?: boolean;
|
||||
export const ReindexFlyoutStep: React.FunctionComponent<{
|
||||
closeFlyout: () => void;
|
||||
reindexState: ReindexState;
|
||||
startReindex: () => void;
|
||||
cancelReindex: () => void;
|
||||
}> = ({ frozen, closeFlyout, reindexState, startReindex, cancelReindex }) => {
|
||||
}> = ({ closeFlyout, reindexState, startReindex, cancelReindex }) => {
|
||||
const {
|
||||
services: {
|
||||
api,
|
||||
|
@ -96,13 +98,14 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
return (
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
{reindexState.meta.isFrozen && <FrozenCallOut />}
|
||||
{hasRequiredPrivileges === false && (
|
||||
<Fragment>
|
||||
<EuiSpacer />
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.insufficientPrivilegeCallout.calloutTitle"
|
||||
defaultMessage="You do not have sufficient privileges to reindex this index"
|
||||
/>
|
||||
}
|
||||
|
@ -111,7 +114,6 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{nodes && nodes.length > 0 && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
|
@ -120,14 +122,14 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
data-test-subj="lowDiskSpaceCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.lowDiskSpaceCalloutTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.lowDiskSpaceCalloutTitle"
|
||||
defaultMessage="Nodes with low disk space"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.lowDiskSpaceCalloutDescription"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.lowDiskSpaceCalloutDescription"
|
||||
defaultMessage="Disk usage has exceeded the low watermark, which may prevent reindexing. The following nodes are impacted:"
|
||||
/>
|
||||
|
||||
|
@ -137,7 +139,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
{nodes.map(({ nodeName, available, nodeId }) => (
|
||||
<li key={nodeId} data-test-subj="impactedNodeListItem">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.lowDiskSpaceUsedText"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.lowDiskSpaceUsedText"
|
||||
defaultMessage="{nodeName} ({available} available)"
|
||||
values={{
|
||||
nodeName,
|
||||
|
@ -152,43 +154,20 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
{(hasFetchFailed || hasReindexingFailed) && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj={hasFetchFailed ? 'fetchFailedCallout' : 'reindexingFailedCallout'}
|
||||
title={
|
||||
hasFetchFailed ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.fetchFailedCalloutTitle"
|
||||
defaultMessage="Reindex status not available"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexingFailedCalloutTitle"
|
||||
defaultMessage="Reindexing error"
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
{reindexState.errorMessage}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
{hasFetchFailed && <FetchFailedCallOut errorMessage={reindexState.errorMessage!} />}
|
||||
{!hasFetchFailed && hasReindexingFailed && (
|
||||
<ReindexingFailedCallOut errorMessage={reindexState.errorMessage!} />
|
||||
)}
|
||||
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexDescription"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexDescription"
|
||||
defaultMessage="The index will be read-only during reindexing. You won't be able to add, update, or delete documents until reindexing is complete. If you need to reindex to a new cluster, use the reindex API. {docsLink}"
|
||||
values={{
|
||||
docsLink: (
|
||||
<EuiLink target="_blank" href={docLinks.links.upgradeAssistant.remoteReindex}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.checkupTab.reindexing.flyout.learnMoreLinkLabel',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.learnMoreLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Learn more',
|
||||
}
|
||||
|
@ -198,40 +177,9 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
}}
|
||||
/>
|
||||
</p>
|
||||
{frozen && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexFrozenIndexTitle"
|
||||
defaultMessage="This index is frozen"
|
||||
/>
|
||||
}
|
||||
iconType="iInCircle"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.reindexFrozenIndex"
|
||||
defaultMessage="Frozen indices will no longer be supported after upgrade, so this index will be deleted as part the reindex operation. {docsLink}"
|
||||
values={{
|
||||
docsLink: (
|
||||
<EuiLink target="_blank" href={docLinks.links.upgradeAssistant.unfreezeApi}>
|
||||
{i18n.translate(
|
||||
'xpack.upgradeAssistant.checkupTab.reindexing.flyout.learnMoreLinkLabel',
|
||||
{
|
||||
defaultMessage: 'Learn more',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.readonlyCallout.backgroundResumeDetail"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.readonlyCallout.backgroundResumeDetail"
|
||||
defaultMessage="Reindexing is performed in the background. You can return to the Upgrade Assistant to view progress or resume reindexing after a Kibana restart."
|
||||
/>
|
||||
</p>
|
||||
|
@ -244,7 +192,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.closeButtonLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
interface Props {
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export const ReindexingFailedCallOut: React.FunctionComponent<Props> = (props) => {
|
||||
const { errorMessage } = props;
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiCallOut
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj="reindexingFailedCallout"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.reindexStep.reindexingFailedCalloutTitle"
|
||||
defaultMessage="Reindexing error"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{errorMessage}
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type FlyoutStep =
|
||||
| 'details'
|
||||
| 'confirmReadonly'
|
||||
| 'confirmReindex'
|
||||
| 'reindexing'
|
||||
| 'makeReadonly'
|
||||
| 'unfreeze'
|
||||
| 'completed';
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { UpdateIndexFlyoutStep } from './update_step';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
import type { UpdateIndexState } from '../../../use_update_index';
|
||||
|
||||
describe('UpdateIndexFlyoutStep', () => {
|
||||
const meta: ReindexState['meta'] = {
|
||||
indexName: 'some_index',
|
||||
aliases: [],
|
||||
isInDataStream: false,
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isClosedIndex: false,
|
||||
reindexName: 'some_index-reindexed-for-9',
|
||||
};
|
||||
|
||||
const defaultUpdateIndexState: UpdateIndexState = {
|
||||
status: 'incomplete',
|
||||
failedBefore: false,
|
||||
};
|
||||
|
||||
it('renders makeReadonly operation', () => {
|
||||
const wrapper = shallow(
|
||||
<UpdateIndexFlyoutStep
|
||||
action="makeReadonly"
|
||||
closeFlyout={jest.fn()}
|
||||
meta={meta}
|
||||
retry={jest.fn()}
|
||||
updateIndexState={defaultUpdateIndexState}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTitle
|
||||
size="xs"
|
||||
>
|
||||
<h3>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Upgrade in progress…"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.title.updateInProgressText"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
<StepProgress
|
||||
steps={
|
||||
Array [
|
||||
Object {
|
||||
"status": "incomplete",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Setting {indexName} index to read-only."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.step.readonlyStepText"
|
||||
values={
|
||||
Object {
|
||||
"indexName": <EuiCode>
|
||||
some_index
|
||||
</EuiCode>,
|
||||
}
|
||||
}
|
||||
/>,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</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>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
});
|
||||
|
||||
it('renders unfreeze operation', () => {
|
||||
const wrapper = shallow(
|
||||
<UpdateIndexFlyoutStep
|
||||
action="unfreeze"
|
||||
closeFlyout={jest.fn()}
|
||||
meta={meta}
|
||||
retry={jest.fn()}
|
||||
updateIndexState={defaultUpdateIndexState}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTitle
|
||||
size="xs"
|
||||
>
|
||||
<h3>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Upgrade in progress…"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.title.updateInProgressText"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
<StepProgress
|
||||
steps={
|
||||
Array [
|
||||
Object {
|
||||
"status": "incomplete",
|
||||
"title": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Unfreezing {indexName} index."
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.step.unfreezeStepText"
|
||||
values={
|
||||
Object {
|
||||
"indexName": <EuiCode>
|
||||
some_index
|
||||
</EuiCode>,
|
||||
}
|
||||
}
|
||||
/>,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</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>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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, { Fragment } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiCode,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import type { UpdateIndexState } from '../../../use_update_index';
|
||||
import { FrozenCallOut } from '../frozen_callout';
|
||||
import { StepProgress, type StepProgressStep } from '../../../../../common/step_progress';
|
||||
import type { ReindexState } from '../../../use_reindex';
|
||||
|
||||
interface UpdateIndexFlyoutStepProps {
|
||||
action: 'unfreeze' | 'makeReadonly';
|
||||
closeFlyout: () => void;
|
||||
meta: ReindexState['meta'];
|
||||
updateIndexState: UpdateIndexState;
|
||||
retry: () => void;
|
||||
}
|
||||
|
||||
const ErrorCallout: React.FunctionComponent<{ reason: string }> = ({ reason }) => (
|
||||
<EuiCallOut color="danger" title="There was an error">
|
||||
<EuiText>
|
||||
<p>{reason}</p>
|
||||
</EuiText>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
||||
/**
|
||||
* In charge of rendering the result of the make read-only calls
|
||||
*/
|
||||
export const UpdateIndexFlyoutStep: React.FunctionComponent<UpdateIndexFlyoutStepProps> = ({
|
||||
action,
|
||||
closeFlyout,
|
||||
meta,
|
||||
updateIndexState,
|
||||
retry,
|
||||
}) => {
|
||||
const { isFrozen, indexName } = meta;
|
||||
const { status, failedBefore, reason } = updateIndexState;
|
||||
const title =
|
||||
action === 'makeReadonly' ? (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.step.readonlyStepText"
|
||||
defaultMessage="Setting {indexName} index to read-only."
|
||||
values={{
|
||||
indexName: <EuiCode>{indexName}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.step.unfreezeStepText"
|
||||
defaultMessage="Unfreezing {indexName} index."
|
||||
values={{
|
||||
indexName: <EuiCode>{indexName}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const steps: StepProgressStep[] = [
|
||||
{
|
||||
title,
|
||||
status,
|
||||
...(reason && { children: <ErrorCallout {...{ reason }} /> }),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiFlyoutBody>
|
||||
{isFrozen && <FrozenCallOut />}
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
{(status === 'inProgress' || status === 'incomplete') && (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.title.updateInProgressText"
|
||||
defaultMessage="Upgrade in progress…"
|
||||
/>
|
||||
)}
|
||||
{status === 'complete' && (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.title.updateCompleteText"
|
||||
defaultMessage="Operation completed"
|
||||
/>
|
||||
)}
|
||||
{status === 'failed' && (
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.checklist.title.updateFailedText"
|
||||
defaultMessage="Operation failed"
|
||||
/>
|
||||
)}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
<StepProgress steps={steps} />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
{status !== 'complete' && failedBefore && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
onClick={retry}
|
||||
isLoading={status === 'inProgress'}
|
||||
disabled={status === 'inProgress'}
|
||||
data-test-subj="startIndexReindexingButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.updateStep.retryButtonLabel"
|
||||
defaultMessage="Retry"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`WarningsFlyoutStep renders 1`] = `
|
||||
exports[`WarningFlyoutStep renders 1`] = `
|
||||
<Fragment>
|
||||
<EuiFlyoutBody />
|
||||
<EuiFlyoutFooter>
|
||||
|
@ -17,7 +17,7 @@ exports[`WarningsFlyoutStep renders 1`] = `
|
|||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Back"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.cancelButtonLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.backButtonLabel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
|
@ -26,13 +26,14 @@ exports[`WarningsFlyoutStep renders 1`] = `
|
|||
>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="startReindexingButton"
|
||||
disabled={false}
|
||||
fill={true}
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Continue reindexing"
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.continueButtonLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.reindex.continueButtonLabel"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
|
@ -10,12 +10,12 @@ import { mount, shallow } from 'enzyme';
|
|||
import React from 'react';
|
||||
import SemVer from 'semver/classes/semver';
|
||||
|
||||
import { ReindexWarning } from '../../../../../../../common/types';
|
||||
import { idForWarning, WarningsFlyoutStep } from './warnings_step';
|
||||
import { idForWarning, WarningFlyoutStep } from './warning_step';
|
||||
import { IndexWarning } from '../../../../../../../../../common/types';
|
||||
|
||||
const kibanaVersion = new SemVer('8.0.0');
|
||||
|
||||
jest.mock('../../../../../app_context', () => {
|
||||
jest.mock('../../../../../../../app_context', () => {
|
||||
const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks');
|
||||
|
||||
return {
|
||||
|
@ -31,20 +31,24 @@ jest.mock('../../../../../app_context', () => {
|
|||
};
|
||||
});
|
||||
|
||||
describe('WarningsFlyoutStep', () => {
|
||||
describe('WarningFlyoutStep', () => {
|
||||
const defaultProps = {
|
||||
warnings: [] as ReindexWarning[],
|
||||
hideWarningsStep: jest.fn(),
|
||||
continueReindex: jest.fn(),
|
||||
warnings: [] as IndexWarning[],
|
||||
back: jest.fn(),
|
||||
confirm: jest.fn(),
|
||||
flow: 'reindex' as const,
|
||||
meta: {
|
||||
indexName: 'foo',
|
||||
reindexName: 'reindexed-foo',
|
||||
aliases: [],
|
||||
isFrozen: false,
|
||||
isReadonly: false,
|
||||
isInDataStream: false,
|
||||
},
|
||||
};
|
||||
|
||||
it('renders', () => {
|
||||
expect(shallow(<WarningsFlyoutStep {...defaultProps} />)).toMatchSnapshot();
|
||||
expect(shallow(<WarningFlyoutStep {...defaultProps} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
if (kibanaVersion.major === 7) {
|
||||
|
@ -53,28 +57,29 @@ describe('WarningsFlyoutStep', () => {
|
|||
...defaultProps,
|
||||
warnings: [
|
||||
{
|
||||
flow: 'all' as const,
|
||||
warningType: 'indexSetting',
|
||||
meta: {
|
||||
deprecatedSettings: ['index.force_memory_term_dictionary'],
|
||||
},
|
||||
},
|
||||
] as ReindexWarning[],
|
||||
] as IndexWarning[],
|
||||
};
|
||||
const wrapper = mount(
|
||||
<I18nProvider>
|
||||
<WarningsFlyoutStep {...defaultPropsWithWarnings} />
|
||||
<WarningFlyoutStep {...defaultPropsWithWarnings} />
|
||||
</I18nProvider>
|
||||
);
|
||||
const button = wrapper.find('EuiButton');
|
||||
|
||||
button.simulate('click');
|
||||
expect(defaultPropsWithWarnings.continueReindex).not.toHaveBeenCalled();
|
||||
expect(defaultPropsWithWarnings.confirm).not.toHaveBeenCalled();
|
||||
|
||||
// first warning (indexSetting)
|
||||
wrapper.find(`input#${idForWarning(1)}`).simulate('change');
|
||||
button.simulate('click');
|
||||
|
||||
expect(defaultPropsWithWarnings.continueReindex).toHaveBeenCalled();
|
||||
expect(defaultPropsWithWarnings.confirm).toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import {
|
||||
IndexWarning,
|
||||
IndexWarningType,
|
||||
ReindexStatusResponse,
|
||||
} from '../../../../../../../../../common/types';
|
||||
import { useAppContext } from '../../../../../../../app_context';
|
||||
import {
|
||||
DeprecatedSettingWarningCheckbox,
|
||||
ReplaceIndexWithAliasWarningCheckbox,
|
||||
MakeIndexReadonlyWarningCheckbox,
|
||||
WarningCheckboxProps,
|
||||
} from './warning_step_checkbox';
|
||||
import { FrozenCallOut } from '../frozen_callout';
|
||||
|
||||
interface CheckedIds {
|
||||
[id: string]: boolean;
|
||||
}
|
||||
|
||||
const warningToComponentMap: {
|
||||
[key in IndexWarningType]: React.FunctionComponent<WarningCheckboxProps>;
|
||||
} = {
|
||||
indexSetting: DeprecatedSettingWarningCheckbox,
|
||||
replaceIndexWithAlias: ReplaceIndexWithAliasWarningCheckbox,
|
||||
makeIndexReadonly: MakeIndexReadonlyWarningCheckbox,
|
||||
};
|
||||
|
||||
export const idForWarning = (id: number) => `reindexWarning-${id}`;
|
||||
interface WarningFlyoutStepProps {
|
||||
back: () => void;
|
||||
confirm: () => void;
|
||||
flow: 'readonly' | 'reindex';
|
||||
meta: ReindexStatusResponse['meta'];
|
||||
warnings: IndexWarning[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays warning text about destructive changes required to reindex this index. The user
|
||||
* must acknowledge each change before being allowed to proceed.
|
||||
*/
|
||||
export const WarningFlyoutStep: React.FunctionComponent<WarningFlyoutStepProps> = ({
|
||||
back,
|
||||
confirm,
|
||||
flow,
|
||||
meta,
|
||||
warnings,
|
||||
}) => {
|
||||
const {
|
||||
services: {
|
||||
core: { docLinks },
|
||||
},
|
||||
} = useAppContext();
|
||||
const { links } = docLinks;
|
||||
|
||||
const [checkedIds, setCheckedIds] = useState<CheckedIds>(
|
||||
warnings.reduce((initialCheckedIds, warning, index) => {
|
||||
initialCheckedIds[idForWarning(index)] = false;
|
||||
return initialCheckedIds;
|
||||
}, {} as { [id: string]: boolean })
|
||||
);
|
||||
|
||||
// Do not allow to proceed until all checkboxes are checked.
|
||||
const blockAdvance = Object.values(checkedIds).filter((v) => v).length < warnings.length;
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const optionId = e.target.id;
|
||||
|
||||
setCheckedIds((prev) => ({
|
||||
...prev,
|
||||
...{
|
||||
[optionId]: !checkedIds[optionId],
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutBody>
|
||||
{meta.isFrozen && <FrozenCallOut />}
|
||||
{warnings.length > 0 && (
|
||||
<>
|
||||
{flow === 'reindex' && (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.reindex.calloutTitle"
|
||||
defaultMessage="This index requires destructive changes that cannot be reversed"
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.reindex.calloutDetail"
|
||||
defaultMessage="Back up the index before continuing. To proceed with the reindex, accept each change."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
)}
|
||||
{flow === 'readonly' && (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.readonly.calloutTitle"
|
||||
defaultMessage="Enable compatibility by marking this index as read-only"
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.readonly.calloutDetail"
|
||||
defaultMessage="You can enable compatibility with the next version by marking the index as read-only. Note that any attempts to insert new documents or update existing ones will fail. You can choose to reindex after upgrading if needed, to convert the index into a writable one."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
)}
|
||||
<EuiSpacer />
|
||||
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="Accept changes"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
{warnings.map((warning, index) => {
|
||||
const WarningCheckbox = warningToComponentMap[warning.warningType];
|
||||
return (
|
||||
<WarningCheckbox
|
||||
key={idForWarning(index)}
|
||||
isChecked={checkedIds[idForWarning(index)]}
|
||||
onChange={onChange}
|
||||
docLinks={links}
|
||||
id={idForWarning(index)}
|
||||
meta={{ ...meta, ...warning.meta }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="arrowLeft" onClick={back} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.backButtonLabel"
|
||||
defaultMessage="Back"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{flow === 'reindex' && (
|
||||
<EuiButton
|
||||
fill
|
||||
color="primary"
|
||||
onClick={confirm}
|
||||
disabled={blockAdvance}
|
||||
data-test-subj="startReindexingButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.reindex.continueButtonLabel"
|
||||
defaultMessage="Continue reindexing"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
{flow === 'readonly' && (
|
||||
<EuiButton
|
||||
fill
|
||||
color="primary"
|
||||
onClick={confirm}
|
||||
disabled={blockAdvance}
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.readonly.continueButtonLabel"
|
||||
data-test-subj="startIndexReadonlyButton"
|
||||
defaultMessage="Mark as read-only"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -19,11 +19,11 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { DocLinksStart } from '@kbn/core/public';
|
||||
import { ReindexWarning, ReindexWarningTypes } from '../../../../../../../common/types';
|
||||
import { IndexWarning, IndexWarningType } from '../../../../../../../../../common/types';
|
||||
|
||||
export const hasReindexWarning = (
|
||||
warnings: ReindexWarning[],
|
||||
warningType: ReindexWarningTypes
|
||||
export const hasIndexWarning = (
|
||||
warnings: IndexWarning[],
|
||||
warningType: IndexWarningType
|
||||
): boolean => {
|
||||
return Boolean(warnings.find((warning) => warning.warningType === warningType));
|
||||
};
|
||||
|
@ -53,7 +53,7 @@ const WarningCheckbox: React.FunctionComponent<{
|
|||
<EuiIconTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.documentationLinkLabel"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.documentationLinkLabel"
|
||||
defaultMessage="Documentation"
|
||||
/>
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export interface WarningCheckboxProps {
|
|||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
docLinks: DocLinksStart['links'];
|
||||
id: string;
|
||||
meta?: ReindexWarning['meta'];
|
||||
meta?: IndexWarning['meta'];
|
||||
}
|
||||
|
||||
export const DeprecatedSettingWarningCheckbox: React.FunctionComponent<WarningCheckboxProps> = ({
|
||||
|
@ -96,14 +96,14 @@ export const DeprecatedSettingWarningCheckbox: React.FunctionComponent<WarningCh
|
|||
warningId={id}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.deprecatedIndexSettingsWarningTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.deprecatedIndexSettingsWarningTitle"
|
||||
defaultMessage="Remove deprecated index settings"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.deprecatedIndexSettingsWarningDetail"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.deprecatedIndexSettingsWarningDetail"
|
||||
defaultMessage="The following deprecated index settings were detected:"
|
||||
/>
|
||||
|
||||
|
@ -135,7 +135,7 @@ export const ReplaceIndexWithAliasWarningCheckbox: React.FunctionComponent<
|
|||
warningId={id}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.replaceIndexWithAliasWarningTitle"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.replaceIndexWithAliasWarningTitle"
|
||||
defaultMessage="Replace {indexName} index with {reindexName} index and create {indexName} index alias"
|
||||
values={{
|
||||
indexName: <EuiCode>{meta?.indexName}</EuiCode>,
|
||||
|
@ -145,7 +145,7 @@ export const ReplaceIndexWithAliasWarningCheckbox: React.FunctionComponent<
|
|||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.replaceIndexWithAliasWarningDetail"
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.replaceIndexWithAliasWarningDetail"
|
||||
defaultMessage="You can search {indexName} as before. To delete the data you'll have to delete {reindexName}"
|
||||
values={{
|
||||
indexName: <EuiCode>{meta?.indexName}</EuiCode>,
|
||||
|
@ -156,3 +156,36 @@ export const ReplaceIndexWithAliasWarningCheckbox: React.FunctionComponent<
|
|||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const MakeIndexReadonlyWarningCheckbox: React.FunctionComponent<WarningCheckboxProps> = ({
|
||||
isChecked,
|
||||
onChange,
|
||||
id,
|
||||
meta,
|
||||
}) => {
|
||||
return (
|
||||
<WarningCheckbox
|
||||
isChecked={isChecked}
|
||||
onChange={onChange}
|
||||
warningId={id}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.makeIndexReadonlyWarningTitle"
|
||||
defaultMessage="Flag {indexName} index as read-only"
|
||||
values={{
|
||||
indexName: <EuiCode>{meta?.indexName}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.warningsStep.makeIndexReadonlyWarningDetail"
|
||||
defaultMessage="You can continue to search and retrieve documents from {indexName}. You will not be able to insert new documents or modify existing ones."
|
||||
values={{
|
||||
indexName: <EuiCode>{meta?.indexName}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { ReindexTableRow } from './table_row';
|
||||
export { IndexTableRow } from './table_row';
|
|
@ -19,65 +19,89 @@ import {
|
|||
import { ReindexStatus } from '../../../../../../common/types';
|
||||
import { getReindexProgressLabel } from '../../../../lib/utils';
|
||||
import { LoadingState } from '../../../types';
|
||||
import { useReindexContext } from './context';
|
||||
import { useIndexContext } from './context';
|
||||
|
||||
const i18nTexts = {
|
||||
reindexLoadingStatusText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexLoadingStatusText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexLoadingStatusText',
|
||||
{
|
||||
defaultMessage: 'Loading status…',
|
||||
}
|
||||
),
|
||||
reindexInProgressText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexInProgressText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexInProgressText',
|
||||
{
|
||||
defaultMessage: 'Reindexing in progress…',
|
||||
}
|
||||
),
|
||||
reindexCompleteText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexCompleteText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexCompleteText',
|
||||
{
|
||||
defaultMessage: 'Reindex complete',
|
||||
}
|
||||
),
|
||||
reindexFailedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexFailedText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexFailedText',
|
||||
{
|
||||
defaultMessage: 'Reindex failed',
|
||||
}
|
||||
),
|
||||
reindexFetchFailedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexFetchFailedText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexFetchFailedText',
|
||||
{
|
||||
defaultMessage: 'Reindex status not available',
|
||||
}
|
||||
),
|
||||
reindexCanceledText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexCanceledText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexCanceledText',
|
||||
{
|
||||
defaultMessage: 'Reindex cancelled',
|
||||
}
|
||||
),
|
||||
reindexPausedText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.reindexPausedText',
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexPausedText',
|
||||
{
|
||||
defaultMessage: 'Reindex paused',
|
||||
}
|
||||
),
|
||||
resolutionText: i18n.translate('xpack.upgradeAssistant.esDeprecations.reindex.resolutionLabel', {
|
||||
reindexText: i18n.translate('xpack.upgradeAssistant.esDeprecations.indices.reindexLabel', {
|
||||
defaultMessage: 'Reindex',
|
||||
}),
|
||||
resolutionTooltipLabel: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.resolutionTooltipLabel',
|
||||
reindexTooltipLabel: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.reindexTooltipLabel',
|
||||
{
|
||||
defaultMessage: 'Resolve this issue by reindexing into a new, compatible index.',
|
||||
}
|
||||
),
|
||||
updateText: i18n.translate('xpack.upgradeAssistant.esDeprecations.indices.updateLabel', {
|
||||
defaultMessage: 'Update',
|
||||
}),
|
||||
updateCompleteText: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.updateCompleteText',
|
||||
{
|
||||
defaultMessage: 'Update complete',
|
||||
}
|
||||
),
|
||||
updateTooltipLabel: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.updateTooltipLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'Resolve this issue by reindexing this index. This issue can be resolved automatically.',
|
||||
'Resolve this issue by updating this index. This issue can be resolved automatically either by marking the index as read-only (recommended for large indices) or by reindexing into a new, compatible index.',
|
||||
}
|
||||
),
|
||||
unfreezeText: i18n.translate('xpack.upgradeAssistant.esDeprecations.indices.unfreezeLabel', {
|
||||
defaultMessage: 'Unfreeze',
|
||||
}),
|
||||
unfreezeTooltipLabel: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.indices.unfreezeTooltipLabel',
|
||||
{
|
||||
defaultMessage: 'Resolve this issue by unfreezing this index.',
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
export const ReindexResolutionCell: React.FunctionComponent = () => {
|
||||
const { reindexState } = useReindexContext();
|
||||
const { reindexState, deprecation, updateIndexState } = useIndexContext();
|
||||
const hasExistingAliases = reindexState.meta.aliases.length > 0;
|
||||
|
||||
if (reindexState.loadingState === LoadingState.Loading) {
|
||||
|
@ -158,14 +182,51 @@ export const ReindexResolutionCell: React.FunctionComponent = () => {
|
|||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiToolTip position="top" content={i18nTexts.resolutionTooltipLabel}>
|
||||
switch (updateIndexState.status) {
|
||||
case 'complete':
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="check" color="success" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.updateCompleteText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
// reindex status "not started"
|
||||
return deprecation.correctiveAction?.type === 'unfreeze' ? (
|
||||
<EuiToolTip position="top" content={i18nTexts.unfreezeTooltipLabel}>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="indexSettings" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.resolutionText}</EuiText>
|
||||
<EuiText size="s">{i18nTexts.unfreezeText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiToolTip>
|
||||
) : reindexState.meta.isReadonly ? (
|
||||
<EuiToolTip position="top" content={i18nTexts.reindexTooltipLabel}>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="indexSettings" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.reindexText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
<EuiToolTip position="top" content={i18nTexts.updateTooltipLabel}>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="indexSettings" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{i18nTexts.updateText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiToolTip>
|
|
@ -19,8 +19,8 @@ import {
|
|||
import { DeprecationTableColumns } from '../../../types';
|
||||
import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells';
|
||||
import { ReindexResolutionCell } from './resolution_table_cell';
|
||||
import { ReindexFlyout, ReindexFlyoutProps } from './flyout';
|
||||
import { ReindexStatusProvider, useReindexContext } from './context';
|
||||
import { IndexFlyout, IndexFlyoutProps } from './flyout';
|
||||
import { IndexStatusProvider, useIndexContext } from './context';
|
||||
|
||||
const { useGlobalFlyout } = GlobalFlyout;
|
||||
|
||||
|
@ -29,31 +29,30 @@ interface TableRowProps {
|
|||
rowFieldNames: DeprecationTableColumns[];
|
||||
}
|
||||
|
||||
const ReindexTableRowCells: React.FunctionComponent<TableRowProps> = ({
|
||||
const IndexTableRowCells: React.FunctionComponent<TableRowProps> = ({
|
||||
rowFieldNames,
|
||||
deprecation,
|
||||
}) => {
|
||||
const [showFlyout, setShowFlyout] = useState(false);
|
||||
const reindexState = useReindexContext();
|
||||
const indexContext = useIndexContext();
|
||||
|
||||
const { addContent: addContentToGlobalFlyout, removeContent: removeContentFromGlobalFlyout } =
|
||||
useGlobalFlyout();
|
||||
|
||||
const closeFlyout = useCallback(async () => {
|
||||
removeContentFromGlobalFlyout('reindexFlyout');
|
||||
removeContentFromGlobalFlyout('indexFlyout');
|
||||
setShowFlyout(false);
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_CLOSE_FLYOUT_CLICK);
|
||||
}, [removeContentFromGlobalFlyout]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showFlyout) {
|
||||
addContentToGlobalFlyout<ReindexFlyoutProps>({
|
||||
id: 'reindexFlyout',
|
||||
Component: ReindexFlyout,
|
||||
addContentToGlobalFlyout<IndexFlyoutProps>({
|
||||
id: 'indexFlyout',
|
||||
Component: IndexFlyout,
|
||||
props: {
|
||||
deprecation,
|
||||
closeFlyout,
|
||||
...reindexState,
|
||||
...indexContext,
|
||||
},
|
||||
flyoutProps: {
|
||||
onClose: closeFlyout,
|
||||
|
@ -63,7 +62,7 @@ const ReindexTableRowCells: React.FunctionComponent<TableRowProps> = ({
|
|||
},
|
||||
});
|
||||
}
|
||||
}, [addContentToGlobalFlyout, deprecation, showFlyout, reindexState, closeFlyout]);
|
||||
}, [addContentToGlobalFlyout, deprecation, showFlyout, indexContext, closeFlyout]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showFlyout) {
|
||||
|
@ -93,14 +92,14 @@ const ReindexTableRowCells: React.FunctionComponent<TableRowProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const ReindexTableRow: React.FunctionComponent<TableRowProps> = (props) => {
|
||||
export const IndexTableRow: React.FunctionComponent<TableRowProps> = (props) => {
|
||||
const {
|
||||
services: { api },
|
||||
} = useAppContext();
|
||||
|
||||
return (
|
||||
<ReindexStatusProvider indexName={props.deprecation.index!} api={api}>
|
||||
<ReindexTableRowCells {...props} />
|
||||
</ReindexStatusProvider>
|
||||
<IndexStatusProvider deprecation={props.deprecation} api={api}>
|
||||
<IndexTableRowCells {...props} />
|
||||
</IndexStatusProvider>
|
||||
);
|
||||
};
|
|
@ -11,7 +11,7 @@ import {
|
|||
ReindexStatusResponse,
|
||||
ReindexStatus,
|
||||
ReindexStep,
|
||||
ReindexWarning,
|
||||
IndexWarning,
|
||||
} from '../../../../../../common/types';
|
||||
import { CancelLoadingState, LoadingState } from '../../../types';
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
|
@ -25,20 +25,24 @@ export interface ReindexState {
|
|||
status?: ReindexStatus;
|
||||
reindexTaskPercComplete: number | null;
|
||||
errorMessage: string | null;
|
||||
reindexWarnings?: ReindexWarning[];
|
||||
reindexWarnings?: IndexWarning[];
|
||||
hasRequiredPrivileges?: boolean;
|
||||
meta: {
|
||||
indexName: string;
|
||||
reindexName: string;
|
||||
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.
|
||||
|
@ -49,7 +53,6 @@ const getReindexState = (
|
|||
meta: { ...meta, aliases },
|
||||
loadingState: LoadingState.Success,
|
||||
};
|
||||
|
||||
if (warnings) {
|
||||
newReindexState.reindexWarnings = warnings;
|
||||
}
|
||||
|
@ -107,15 +110,32 @@ const getReindexState = (
|
|||
return newReindexState;
|
||||
};
|
||||
|
||||
export const useReindexStatus = ({ 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,
|
||||
reindexTaskPercComplete: null,
|
||||
meta: {
|
||||
indexName,
|
||||
reindexName: '', // will be known after fetching the reindexStatus
|
||||
aliases: [], // will be known after fetching the reindexStatus
|
||||
// these properties will be known after fetching the reindexStatus
|
||||
reindexName: '',
|
||||
aliases: [],
|
||||
isFrozen,
|
||||
isInDataStream,
|
||||
isClosedIndex,
|
||||
isReadonly: false, // we don't have this information in the deprecation list
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 { useCallback, useState } from 'react';
|
||||
import type { UpdateIndexOperation } from '../../../../../../common/update_index';
|
||||
import type { CorrectiveAction } from '../../../../../../common/types';
|
||||
import type { ApiService } from '../../../../lib/api';
|
||||
|
||||
export interface UpdateIndexState {
|
||||
failedBefore: boolean;
|
||||
status: 'incomplete' | 'inProgress' | 'complete' | 'failed';
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface UseUpdateIndexParams {
|
||||
indexName: string;
|
||||
api: ApiService;
|
||||
correctiveAction?: CorrectiveAction;
|
||||
}
|
||||
|
||||
export const useUpdateIndex = ({ indexName, api, correctiveAction }: UseUpdateIndexParams) => {
|
||||
const [failedState, setFailedState] = useState<boolean>(false);
|
||||
const [updateIndexState, setUpdateIndexState] = useState<UpdateIndexState>({
|
||||
failedBefore: false,
|
||||
status: 'incomplete',
|
||||
});
|
||||
|
||||
const updateIndex = useCallback(async () => {
|
||||
const operations: UpdateIndexOperation[] =
|
||||
correctiveAction?.type === 'unfreeze' ? ['unfreeze'] : ['blockWrite', 'unfreeze'];
|
||||
|
||||
setUpdateIndexState({ status: 'inProgress', failedBefore: failedState });
|
||||
const res = await api.updateIndex(indexName, operations);
|
||||
const status = res.error ? 'failed' : 'complete';
|
||||
const failedBefore = failedState || status === 'failed';
|
||||
setFailedState(failedBefore);
|
||||
setUpdateIndexState({
|
||||
status,
|
||||
failedBefore,
|
||||
...(res.error && { reason: res.error.message.toString() }),
|
||||
});
|
||||
}, [api, correctiveAction, failedState, indexName]);
|
||||
|
||||
return {
|
||||
updateIndexState,
|
||||
updateIndex,
|
||||
};
|
||||
};
|
|
@ -1,56 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext } from 'react';
|
||||
|
||||
import { ApiService } from '../../../../lib/api';
|
||||
import { useReindexStatus, ReindexState } from './use_reindex_state';
|
||||
|
||||
export interface ReindexStateContext {
|
||||
reindexState: ReindexState;
|
||||
startReindex: () => Promise<void>;
|
||||
cancelReindex: () => Promise<void>;
|
||||
}
|
||||
|
||||
const ReindexContext = createContext<ReindexStateContext | undefined>(undefined);
|
||||
|
||||
export const useReindexContext = () => {
|
||||
const context = useContext(ReindexContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useReindexContext must be used within a <ReindexStatusProvider />');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
api: ApiService;
|
||||
children: React.ReactNode;
|
||||
indexName: string;
|
||||
}
|
||||
|
||||
export const ReindexStatusProvider: React.FunctionComponent<Props> = ({
|
||||
api,
|
||||
indexName,
|
||||
children,
|
||||
}) => {
|
||||
const { reindexState, startReindex, cancelReindex } = useReindexStatus({
|
||||
indexName,
|
||||
api,
|
||||
});
|
||||
|
||||
return (
|
||||
<ReindexContext.Provider
|
||||
value={{
|
||||
reindexState,
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ReindexContext.Provider>
|
||||
);
|
||||
};
|
|
@ -1,106 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiFlyoutHeader, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
|
||||
import { EnrichedDeprecationInfo, ReindexStatus } from '../../../../../../../common/types';
|
||||
|
||||
import type { ReindexStateContext } from '../context';
|
||||
import { ChecklistFlyoutStep } from './checklist_step';
|
||||
import { WarningsFlyoutStep } from './warnings_step';
|
||||
import { DeprecationBadge } from '../../../../shared';
|
||||
import {
|
||||
UIM_REINDEX_START_CLICK,
|
||||
UIM_REINDEX_STOP_CLICK,
|
||||
uiMetricService,
|
||||
} from '../../../../../lib/ui_metric';
|
||||
|
||||
export interface ReindexFlyoutProps extends ReindexStateContext {
|
||||
deprecation: EnrichedDeprecationInfo;
|
||||
closeFlyout: () => void;
|
||||
}
|
||||
|
||||
export const ReindexFlyout: React.FunctionComponent<ReindexFlyoutProps> = ({
|
||||
reindexState,
|
||||
startReindex,
|
||||
cancelReindex,
|
||||
closeFlyout,
|
||||
deprecation,
|
||||
}) => {
|
||||
const { status, reindexWarnings } = reindexState;
|
||||
const { index } = deprecation;
|
||||
|
||||
const [showWarningsStep, setShowWarningsStep] = useState(false);
|
||||
|
||||
const onStartReindex = useCallback(() => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_START_CLICK);
|
||||
startReindex();
|
||||
}, [startReindex]);
|
||||
|
||||
const onStopReindex = useCallback(() => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_STOP_CLICK);
|
||||
cancelReindex();
|
||||
}, [cancelReindex]);
|
||||
|
||||
const startReindexWithWarnings = () => {
|
||||
if (
|
||||
reindexWarnings &&
|
||||
reindexWarnings.length > 0 &&
|
||||
status !== ReindexStatus.inProgress &&
|
||||
status !== ReindexStatus.completed
|
||||
) {
|
||||
setShowWarningsStep(true);
|
||||
} else {
|
||||
onStartReindex();
|
||||
}
|
||||
};
|
||||
const flyoutContents = showWarningsStep ? (
|
||||
<WarningsFlyoutStep
|
||||
warnings={reindexState.reindexWarnings ?? []}
|
||||
meta={reindexState.meta}
|
||||
hideWarningsStep={() => setShowWarningsStep(false)}
|
||||
continueReindex={() => {
|
||||
setShowWarningsStep(false);
|
||||
onStartReindex();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ChecklistFlyoutStep
|
||||
frozen={deprecation.frozen}
|
||||
closeFlyout={closeFlyout}
|
||||
startReindex={startReindexWithWarnings}
|
||||
reindexState={reindexState}
|
||||
cancelReindex={onStopReindex}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<DeprecationBadge
|
||||
isCritical={deprecation.isCritical}
|
||||
isResolved={status === ReindexStatus.completed}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiTitle size="s" data-test-subj="flyoutTitle">
|
||||
<h2 id="reindexDetailsFlyoutTitle">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.flyoutHeader"
|
||||
defaultMessage="Reindex {index}"
|
||||
values={{ index }}
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
|
||||
{flyoutContents}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,162 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import {
|
||||
ReindexWarning,
|
||||
ReindexWarningTypes,
|
||||
ReindexStatusResponse,
|
||||
} from '../../../../../../../common/types';
|
||||
import { useAppContext } from '../../../../../app_context';
|
||||
import {
|
||||
DeprecatedSettingWarningCheckbox,
|
||||
ReplaceIndexWithAliasWarningCheckbox,
|
||||
WarningCheckboxProps,
|
||||
} from './warning_step_checkbox';
|
||||
|
||||
interface CheckedIds {
|
||||
[id: string]: boolean;
|
||||
}
|
||||
|
||||
const warningToComponentMap: {
|
||||
[key in ReindexWarningTypes]: React.FunctionComponent<WarningCheckboxProps>;
|
||||
} = {
|
||||
indexSetting: DeprecatedSettingWarningCheckbox,
|
||||
replaceIndexWithAlias: ReplaceIndexWithAliasWarningCheckbox,
|
||||
};
|
||||
|
||||
export const idForWarning = (id: number) => `reindexWarning-${id}`;
|
||||
interface WarningsConfirmationFlyoutProps {
|
||||
hideWarningsStep: () => void;
|
||||
continueReindex: () => void;
|
||||
warnings: ReindexWarning[];
|
||||
meta: ReindexStatusResponse['meta'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays warning text about destructive changes required to reindex this index. The user
|
||||
* must acknowledge each change before being allowed to proceed.
|
||||
*/
|
||||
export const WarningsFlyoutStep: React.FunctionComponent<WarningsConfirmationFlyoutProps> = ({
|
||||
warnings,
|
||||
hideWarningsStep,
|
||||
continueReindex,
|
||||
meta,
|
||||
}) => {
|
||||
const {
|
||||
services: {
|
||||
core: { docLinks },
|
||||
},
|
||||
} = useAppContext();
|
||||
const { links } = docLinks;
|
||||
|
||||
const [checkedIds, setCheckedIds] = useState<CheckedIds>(
|
||||
warnings.reduce((initialCheckedIds, warning, index) => {
|
||||
initialCheckedIds[idForWarning(index)] = false;
|
||||
return initialCheckedIds;
|
||||
}, {} as { [id: string]: boolean })
|
||||
);
|
||||
|
||||
// Do not allow to proceed until all checkboxes are checked.
|
||||
const blockAdvance = Object.values(checkedIds).filter((v) => v).length < warnings.length;
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const optionId = e.target.id;
|
||||
|
||||
setCheckedIds((prev) => ({
|
||||
...prev,
|
||||
...{
|
||||
[optionId]: !checkedIds[optionId],
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutBody>
|
||||
{warnings.length > 0 && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutTitle"
|
||||
defaultMessage="This index requires destructive changes that cannot be reversed"
|
||||
/>
|
||||
}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.destructiveCallout.calloutDetail"
|
||||
defaultMessage="Back up the index before continuing. To proceed with the reindex, accept each change."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.warningsStep.acceptChangesTitle"
|
||||
defaultMessage="Accept changes"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
{warnings.map((warning, index) => {
|
||||
const WarningCheckbox = warningToComponentMap[warning.warningType];
|
||||
return (
|
||||
<WarningCheckbox
|
||||
key={idForWarning(index)}
|
||||
isChecked={checkedIds[idForWarning(index)]}
|
||||
onChange={onChange}
|
||||
docLinks={links}
|
||||
id={idForWarning(index)}
|
||||
meta={{ ...meta, ...warning.meta }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="arrowLeft" onClick={hideWarningsStep} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.cancelButtonLabel"
|
||||
defaultMessage="Back"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton fill color="primary" onClick={continueReindex} disabled={blockAdvance}>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.checkupTab.reindexing.flyout.checklistStep.continueButtonLabel"
|
||||
defaultMessage="Continue reindexing"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -31,7 +31,7 @@ import {
|
|||
MlSnapshotsTableRow,
|
||||
DefaultTableRow,
|
||||
IndexSettingsTableRow,
|
||||
ReindexTableRow,
|
||||
IndexTableRow,
|
||||
ClusterSettingsTableRow,
|
||||
HealthIndicatorTableRow,
|
||||
DataStreamTableRow,
|
||||
|
@ -126,7 +126,8 @@ const renderTableRowCells = (
|
|||
return <ClusterSettingsTableRow deprecation={deprecation} rowFieldNames={cellTypes} />;
|
||||
|
||||
case 'reindex':
|
||||
return <ReindexTableRow deprecation={deprecation} rowFieldNames={cellTypes} />;
|
||||
case 'unfreeze':
|
||||
return <IndexTableRow deprecation={deprecation} rowFieldNames={cellTypes} />;
|
||||
|
||||
case 'healthIndicator':
|
||||
return <HealthIndicatorTableRow deprecation={deprecation} rowFieldNames={cellTypes} />;
|
||||
|
|
|
@ -28,7 +28,7 @@ const i18nTexts = {
|
|||
}
|
||||
),
|
||||
manualCellTooltipLabel: i18n.translate(
|
||||
'xpack.upgradeAssistant.esDeprecations.reindex.manualCellTooltipLabel',
|
||||
'xpack.upgradeAssistant.esDeprecations.defaultDeprecation.manualCellTooltipLabel',
|
||||
{
|
||||
defaultMessage: 'This issue needs to be resolved manually.',
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { HttpSetup } from '@kbn/core/public';
|
||||
import type { HttpSetup } from '@kbn/core/public';
|
||||
|
||||
import {
|
||||
import type { UpdateIndexOperation } from '../../../common/update_index';
|
||||
import type {
|
||||
ESUpgradeStatus,
|
||||
CloudBackupStatus,
|
||||
ClusterUpgradeState,
|
||||
|
@ -24,9 +25,9 @@ import {
|
|||
CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS,
|
||||
} from '../../../common/constants';
|
||||
import {
|
||||
UseRequestConfig,
|
||||
SendRequestConfig,
|
||||
SendRequestResponse,
|
||||
type UseRequestConfig,
|
||||
type SendRequestConfig,
|
||||
type SendRequestResponse,
|
||||
sendRequest as _sendRequest,
|
||||
useRequest as _useRequest,
|
||||
} from '../../shared_imports';
|
||||
|
@ -211,41 +212,51 @@ export class ApiService {
|
|||
});
|
||||
}
|
||||
|
||||
public async getDataStreamReindexStatus(dataStreamName: string) {
|
||||
/**
|
||||
* Data Stream Migrations
|
||||
* Reindex and readonly operations
|
||||
*/
|
||||
|
||||
public async getDataStreamMigrationStatus(dataStreamName: string) {
|
||||
return await this.sendRequest<DataStreamReindexStatusResponse>({
|
||||
path: `${API_BASE_PATH}/reindex_data_streams/${dataStreamName}`,
|
||||
path: `${API_BASE_PATH}/migrate_data_stream/${dataStreamName}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
public async getDataStreamMetadata(dataStreamName: string) {
|
||||
return await this.sendRequest<DataStreamMetadata>({
|
||||
path: `${API_BASE_PATH}/reindex_data_streams/${dataStreamName}/metadata`,
|
||||
path: `${API_BASE_PATH}/migrate_data_stream/${dataStreamName}/metadata`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
public async startDataStreamReindexTask(dataStreamName: string) {
|
||||
return await this.sendRequest({
|
||||
path: `${API_BASE_PATH}/reindex_data_streams/${dataStreamName}`,
|
||||
path: `${API_BASE_PATH}/migrate_data_stream/${dataStreamName}/reindex`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
public async cancelDataStreamReindexTask(dataStreamName: string) {
|
||||
return await this.sendRequest({
|
||||
path: `${API_BASE_PATH}/reindex_data_streams/${dataStreamName}/cancel`,
|
||||
path: `${API_BASE_PATH}/migrate_data_stream/${dataStreamName}/reindex/cancel`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
public async pauseDataStreamReindexTask(dataStreamName: string) {
|
||||
public async markIndicesAsReadOnly(dataStreamName: string, indices: string[]) {
|
||||
return await this.sendRequest({
|
||||
path: `${API_BASE_PATH}/reindex_data_streams/${dataStreamName}/pause`,
|
||||
path: `${API_BASE_PATH}/migrate_data_stream/${dataStreamName}/readonly`,
|
||||
method: 'post',
|
||||
body: { indices },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* FINISH: Data Stream Migrations
|
||||
*/
|
||||
|
||||
public async getReindexStatus(indexName: string) {
|
||||
return await this.sendRequest<ReindexStatusResponse>({
|
||||
path: `${API_BASE_PATH}/reindex/${indexName}`,
|
||||
|
@ -267,6 +278,14 @@ export class ApiService {
|
|||
});
|
||||
}
|
||||
|
||||
public async updateIndex(indexName: string, operations: UpdateIndexOperation[]) {
|
||||
return await this.sendRequest({
|
||||
path: `${API_BASE_PATH}/update_index/${indexName}`,
|
||||
method: 'post',
|
||||
body: { operations },
|
||||
});
|
||||
}
|
||||
|
||||
public useLoadUpgradeStatus() {
|
||||
return this.useRequest<{
|
||||
readyForUpgrade: boolean;
|
||||
|
|
|
@ -19,11 +19,18 @@ export const UIM_REINDEX_OPEN_FLYOUT_CLICK = 'reindex_open_flyout_click';
|
|||
export const UIM_REINDEX_CLOSE_FLYOUT_CLICK = 'reindex_close_flyout_click';
|
||||
export const UIM_REINDEX_START_CLICK = 'reindex_start_click';
|
||||
export const UIM_REINDEX_STOP_CLICK = 'reindex_stop_click';
|
||||
export const UIM_REINDEX_READONLY_CLICK = 'reindex_readonly_click';
|
||||
export const UIM_REINDEX_UNFREEZE_CLICK = 'reindex_unfreeze_click';
|
||||
export const UIM_REINDEX_READONLY_RETRY_CLICK = 'reindex_readonly_retry_click';
|
||||
export const UIM_REINDEX_UNFREEZE_RETRY_CLICK = 'reindex_unfreeze_retry_click';
|
||||
|
||||
// Data Streams Reindexing
|
||||
export const UIM_DATA_STREAM_REINDEX_OPEN_FLYOUT_CLICK = 'data_stream_reindex_open_flyout_click';
|
||||
export const UIM_DATA_STREAM_REINDEX_CLOSE_FLYOUT_CLICK = 'data_stream_reindex_close_flyout_click';
|
||||
export const UIM_DATA_STREAM_REINDEX_START_CLICK = 'data_stream_reindex_start_click';
|
||||
export const UIM_DATA_STREAM_REINDEX_STOP_CLICK = 'data_stream_reindex_stop_click';
|
||||
export const UIM_DATA_STREAM_START_READONLY_CLICK = 'data_stream_readonly_start_click';
|
||||
export const UIM_DATA_STREAM_STOP_READONLY_CLICK = 'data_stream_readonly_stop_click';
|
||||
|
||||
export const UIM_BACKUP_DATA_CLOUD_CLICK = 'backup_data_cloud_click';
|
||||
export const UIM_BACKUP_DATA_ON_PREM_CLICK = 'backup_data_on_prem_click';
|
||||
|
|
|
@ -9,7 +9,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
|
|||
import { tryCatch, fold } from 'fp-ts/lib/Either';
|
||||
|
||||
import { DEPRECATION_WARNING_UPPER_LIMIT } from '../../../common/constants';
|
||||
import { ReindexStep, DataStreamReindexStatus } from '../../../common/types';
|
||||
import { ReindexStep, DataStreamMigrationStatus } from '../../../common/types';
|
||||
|
||||
export const validateRegExpString = (s: string) =>
|
||||
pipe(
|
||||
|
@ -103,20 +103,20 @@ export const getReindexProgressLabel = (
|
|||
};
|
||||
|
||||
export const getDataStreamReindexProgress = (
|
||||
status: DataStreamReindexStatus,
|
||||
reindexTaskPercComplete: number | null
|
||||
status: DataStreamMigrationStatus,
|
||||
taskPercComplete: number | null
|
||||
): number => {
|
||||
switch (status) {
|
||||
case DataStreamReindexStatus.notStarted:
|
||||
case DataStreamMigrationStatus.notStarted:
|
||||
return 0;
|
||||
|
||||
case DataStreamReindexStatus.fetchFailed:
|
||||
case DataStreamReindexStatus.failed:
|
||||
case DataStreamReindexStatus.cancelled:
|
||||
case DataStreamReindexStatus.inProgress: {
|
||||
return reindexTaskPercComplete !== null ? Math.round(reindexTaskPercComplete * 100) : 0;
|
||||
case DataStreamMigrationStatus.fetchFailed:
|
||||
case DataStreamMigrationStatus.failed:
|
||||
case DataStreamMigrationStatus.cancelled:
|
||||
case DataStreamMigrationStatus.inProgress: {
|
||||
return taskPercComplete !== null ? Math.round(taskPercComplete * 100) : 0;
|
||||
}
|
||||
case DataStreamReindexStatus.completed: {
|
||||
case DataStreamMigrationStatus.completed: {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
@ -125,9 +125,9 @@ export const getDataStreamReindexProgress = (
|
|||
};
|
||||
|
||||
export const getDataStreamReindexProgressLabel = (
|
||||
status: DataStreamReindexStatus,
|
||||
reindexTaskPercComplete: number | null
|
||||
status: DataStreamMigrationStatus,
|
||||
taskPercComplete: number | null
|
||||
): string => {
|
||||
const percentsComplete = getDataStreamReindexProgress(status, reindexTaskPercComplete);
|
||||
const percentsComplete = getDataStreamReindexProgress(status, taskPercComplete);
|
||||
return `${percentsComplete}%`;
|
||||
};
|
||||
|
|
|
@ -21,6 +21,22 @@ const configSchema = schema.object({
|
|||
serverless: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
|
||||
/**
|
||||
* Exlcude certain data streams or indices from getting certain correctiveActions.
|
||||
* The key is the data source name or pattern and the value is an array of corrective actions to exclude.
|
||||
*
|
||||
* Exclude readOnly data sources from getting read-only corrective actions.
|
||||
* This is needed to avoid breaking certain built-in/system functionality that might rely on writing to these data sources.
|
||||
* Example (excludes read-only corrective actions for 7_17_data_stream):
|
||||
* xpack.upgrade_assistant.dataSourceExclusions:
|
||||
* 7_17_data_stream: ["readOnly"]
|
||||
*/
|
||||
dataSourceExclusions: schema.recordOf(
|
||||
schema.string(),
|
||||
schema.arrayOf(schema.oneOf([schema.literal('readOnly'), schema.literal('reindex')])),
|
||||
{ defaultValue: {} }
|
||||
),
|
||||
|
||||
featureSet: schema.object({
|
||||
/**
|
||||
* Ml Snapshot should only be enabled for major version upgrades. Currently this
|
||||
|
@ -33,12 +49,12 @@ const configSchema = schema.object({
|
|||
* to change the constant `MachineLearningField.MIN_CHECKED_SUPPORTED_SNAPSHOT_VERSION`
|
||||
* to something higher than 7.0.0 in the Elasticsearch code.
|
||||
*/
|
||||
mlSnapshots: schema.boolean({ defaultValue: false }),
|
||||
mlSnapshots: schema.boolean({ defaultValue: true }),
|
||||
/**
|
||||
* Migrating system indices should only be enabled for major version upgrades.
|
||||
* Currently this is manually set to `true` on every `x.last` version.
|
||||
*/
|
||||
migrateSystemIndices: schema.boolean({ defaultValue: false }),
|
||||
migrateSystemIndices: schema.boolean({ defaultValue: true }),
|
||||
/**
|
||||
* Deprecations with reindexing corrective actions are only enabled for major version upgrades.
|
||||
* Currently this is manually set to `true` on every `x.last` version.
|
||||
|
@ -46,12 +62,12 @@ const configSchema = schema.object({
|
|||
* The reindex action includes some logic that is specific to the 8.0 upgrade
|
||||
* End users could get into a bad situation if this is enabled before this logic is fixed.
|
||||
*/
|
||||
reindexCorrectiveActions: schema.boolean({ defaultValue: false }),
|
||||
reindexCorrectiveActions: schema.boolean({ defaultValue: true }),
|
||||
/**
|
||||
* Migrating deprecated data streams should only be enabled for major version upgrades.
|
||||
* Currently this is manually set to `true` on every `x.last` version.
|
||||
*/
|
||||
migrateDataStreams: schema.boolean({ defaultValue: false }),
|
||||
migrateDataStreams: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
/**
|
||||
* This config allows to hide the UI without disabling the plugin.
|
||||
|
|
|
@ -67,10 +67,12 @@
|
|||
{
|
||||
"level": "critical",
|
||||
"message": "Index created before 7.0",
|
||||
"url":
|
||||
"https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html",
|
||||
"url": "https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html",
|
||||
"details": "This index was created using version: 6.8.13",
|
||||
"resolve_during_rolling_upgrade": false
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"frozen_index": [
|
||||
|
@ -98,17 +100,18 @@
|
|||
"message": "Index created before 7.0",
|
||||
"url": "https: //www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html",
|
||||
"details": "This index was created using version: 6.8.13",
|
||||
"resolve_during_rolling_upgrade": false
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"deprecated_settings": [
|
||||
{
|
||||
"level": "warning",
|
||||
"message": "Setting [index.routing.allocation.include._tier] is deprecated",
|
||||
"url":
|
||||
"https://www.elastic.co/guide/en/elasticsearch/reference/7.16/migrating-7.13.html#deprecate-tier-filter-setting",
|
||||
"details":
|
||||
"Remove the [index.routing.allocation.include._tier] setting. Use [index.routing.allocation.include._tier_preference] to control allocation to data tiers.",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/7.16/migrating-7.13.html#deprecate-tier-filter-setting",
|
||||
"details": "Remove the [index.routing.allocation.include._tier] setting. Use [index.routing.allocation.include._tier_preference] to control allocation to data tiers.",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"actions": [
|
||||
|
@ -167,41 +170,137 @@
|
|||
"resolve_during_rolling_upgrade": false
|
||||
}
|
||||
],
|
||||
"transforms_index": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details": "This index has version: 7.17.25",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true,
|
||||
"transform_ids": ["abc"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"myindex": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details": "This index has version: 7.17.25",
|
||||
"resolve_during_rolling_upgrade": false
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
".ent-search-1": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"url": "https: //www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.0.html#breaking-changes-8.0",
|
||||
"details": "This index has version: 7.17.28-8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
".ent-search-2": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"url": "https: //www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.0.html#breaking-changes-8.0",
|
||||
"details": "This index has version: 7.17.28-8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
".ent-search-3": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"url": "https: //www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.0.html#breaking-changes-8.0",
|
||||
"details": "This index has version: 7.17.28-8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data_streams": {
|
||||
"my-v7-data-stream" : [{
|
||||
"level" : "critical",
|
||||
"message" : "Old data stream with a compatibility version < 8.0",
|
||||
"url" : "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details" : "This data stream has backing indices that were created before Elasticsearch 8.0.0",
|
||||
"resolve_during_rolling_upgrade" : false,
|
||||
"_meta": {
|
||||
"backing_indices": {
|
||||
"count": 52,
|
||||
"need_upgrading": {
|
||||
"count": 37,
|
||||
"searchable_snapshot": {
|
||||
"count": 23,
|
||||
"fully_mounted": {
|
||||
"count": 7
|
||||
},
|
||||
"partially_mounted": {
|
||||
"count": 16
|
||||
}
|
||||
}
|
||||
}
|
||||
"my-v7-data-stream": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old data stream with a compatibility version < 8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details": "This data stream has backing indices that were created before Elasticsearch 8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"indices_requiring_upgrade": [
|
||||
".ds-some-backing-index-5-2024.11.07-000001"
|
||||
],
|
||||
"indices_requiring_upgrade_count": 1,
|
||||
"total_backing_indices": 2,
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
}]
|
||||
],
|
||||
"logs-enterprise_search.default": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old data stream with a compatibility version < 8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details": "This data stream has backing indices that were created before Elasticsearch 8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"indices_requiring_upgrade": [
|
||||
".ds-some-backing-index-5-2024.11.07-000001"
|
||||
],
|
||||
"indices_requiring_upgrade_count": 1,
|
||||
"total_backing_indices": 2,
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"logs-app_search.default": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old data stream with a compatibility version < 8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details": "This data stream has backing indices that were created before Elasticsearch 8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"indices_requiring_upgrade": [
|
||||
".ds-some-backing-index-5-2024.11.07-000001"
|
||||
],
|
||||
"indices_requiring_upgrade_count": 1,
|
||||
"total_backing_indices": 2,
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"logs-workplace_search.default": [
|
||||
{
|
||||
"level": "critical",
|
||||
"message": "Old data stream with a compatibility version < 8.0",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
"details": "This data stream has backing indices that were created before Elasticsearch 8.0.0",
|
||||
"resolve_during_rolling_upgrade": false,
|
||||
"_meta": {
|
||||
"indices_requiring_upgrade": [
|
||||
".ds-some-backing-index-5-2024.11.07-000001"
|
||||
],
|
||||
"indices_requiring_upgrade_count": 1,
|
||||
"total_backing_indices": 2,
|
||||
"reindex_required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ilm_policies": {
|
||||
"myfreezepolicy": [
|
||||
|
@ -224,4 +323,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/types';
|
||||
|
||||
export const diskIndicatorGreen: estypes.HealthReportDiskIndicator = {
|
||||
status: 'green',
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { matchExclusionPattern } from './data_source_exclusions';
|
||||
import { DataSourceExclusions } from '../../common/types';
|
||||
|
||||
describe('matchExclusionPattern', () => {
|
||||
it('should return the actions that should be excluded', () => {
|
||||
const exclusions: DataSourceExclusions = {
|
||||
data_stream_1: ['readOnly'],
|
||||
};
|
||||
|
||||
const result = matchExclusionPattern('data_stream_1', exclusions);
|
||||
expect(result).toEqual(['readOnly']);
|
||||
});
|
||||
|
||||
it('should return an empty array if no exclusions match', () => {
|
||||
const exclusions: DataSourceExclusions = {
|
||||
data_stream_1: ['readOnly'],
|
||||
};
|
||||
|
||||
const result = matchExclusionPattern('data_stream_2', exclusions);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it(`should match patterns ending with '*'`, () => {
|
||||
const exclusions: DataSourceExclusions = {
|
||||
'data_stream_*': ['readOnly'],
|
||||
};
|
||||
|
||||
const result = matchExclusionPattern('data_stream_1', exclusions);
|
||||
expect(result).toEqual(['readOnly']);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 { DataSourceExclusions } from '../../common/types';
|
||||
|
||||
/**
|
||||
* These are the default exclusions for data sources (data streams and indices).
|
||||
*
|
||||
* They are used to exclude migrations from getting certain corrective actions.
|
||||
* This is needed to avoid breaking certain built-in/system functionality that might rely on writing to these data source.
|
||||
*
|
||||
* These indices can be overridden by the user in the Kibana configuration:
|
||||
*
|
||||
* For Example this will renenable all corrective actions for the siem-signals data source:
|
||||
* xpack.upgrade_assistant.dataSourceExclusions:
|
||||
* '.siem-signals*': []
|
||||
*/
|
||||
export const defaultExclusions: DataSourceExclusions = {
|
||||
'.siem-signals*': ['readOnly'],
|
||||
'.alerts*': ['readOnly'],
|
||||
'.internal.alerts*': ['readOnly'],
|
||||
'.preview.alerts*': ['readOnly'],
|
||||
'.internal.preview.alerts*': ['readOnly'],
|
||||
'.lists-*': ['readOnly'],
|
||||
'.items-*': ['readOnly'],
|
||||
'.logs-endpoint.actions-*': ['readOnly'],
|
||||
'.logs-endpoint.action.responses-*': ['readOnly'],
|
||||
'.metrics-endpoint.metadata_united_default': ['readOnly'],
|
||||
'.logs-osquery_manager.actions-*': ['readOnly'],
|
||||
'.logs-osquery_manager.action.responses-*': ['readOnly'],
|
||||
'.logs-endpoint.diagnostic.collection-*': ['readOnly'],
|
||||
'kibana_sample_data_*': ['readOnly'],
|
||||
'.elastic-connectors*': ['readOnly'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Matches the data source name against the exclusion pattern and returns the actions that should be excluded.
|
||||
* If the exclusion ends with a `*` it will match any data source that starts with the excluded pattern.
|
||||
* Otherwise it will match the data source name exactly.
|
||||
*/
|
||||
export const matchExclusionPattern = (dataStreamName: string, exclusions: DataSourceExclusions) => {
|
||||
const result = Object.entries(exclusions).find(([excludedPattern]) => {
|
||||
const isPattern = /.+\*$/.test(excludedPattern);
|
||||
if (isPattern) {
|
||||
const matcher = excludedPattern.slice(0, -1);
|
||||
return dataStreamName.startsWith(matcher);
|
||||
}
|
||||
return dataStreamName === excludedPattern;
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return result[1];
|
||||
};
|
|
@ -13,31 +13,31 @@ import { LicensingPluginSetup } from '@kbn/licensing-plugin/server';
|
|||
import { TransportResult } from '@elastic/elasticsearch';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
DataStreamReindexStatus,
|
||||
DataStreamReindexOperation,
|
||||
DataStreamMigrationStatus,
|
||||
DataStreamMigrationOperation,
|
||||
DataStreamMetadata,
|
||||
DataStreamReindexWarning,
|
||||
DataStreamMigrationWarning,
|
||||
DataStreamReindexTaskStatusResponse,
|
||||
DataStreamReindexStatusCancelled,
|
||||
} from '../../../common/types';
|
||||
|
||||
import { error } from './error';
|
||||
import { DataStreamMigrationError, error } from './error';
|
||||
|
||||
interface DataStreamReindexService {
|
||||
interface DataStreamMigrationService {
|
||||
/**
|
||||
* Checks whether or not the user has proper privileges required to reindex this index.
|
||||
* Checks whether or not the user has proper privileges required to migrate this index.
|
||||
* @param dataStreamName
|
||||
*/
|
||||
hasRequiredPrivileges: (dataStreamName: string) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Checks an index's settings and mappings to flag potential issues during reindex.
|
||||
* Checks an index's settings and mappings to flag potential issues during migration.
|
||||
* Resolves to null if index does not exist.
|
||||
* @param dataStreamName
|
||||
*/
|
||||
detectReindexWarnings: (
|
||||
detectMigrationWarnings: (
|
||||
dataStreamName: string
|
||||
) => Promise<DataStreamReindexWarning[] | undefined>;
|
||||
) => Promise<DataStreamMigrationWarning[] | undefined>;
|
||||
|
||||
/**
|
||||
* Creates a new reindex operation for a given index.
|
||||
|
@ -49,7 +49,7 @@ interface DataStreamReindexService {
|
|||
* Polls Elasticsearch's Data stream status API to retrieve the status of the reindex operation.
|
||||
* @param dataStreamName
|
||||
*/
|
||||
fetchReindexStatus: (dataStreamName: string) => Promise<DataStreamReindexOperation>;
|
||||
fetchMigrationStatus: (dataStreamName: string) => Promise<DataStreamMigrationOperation>;
|
||||
|
||||
/**
|
||||
* Cancels an in-progress reindex operation for a given index.
|
||||
|
@ -62,18 +62,27 @@ interface DataStreamReindexService {
|
|||
* @param dataStreamName
|
||||
*/
|
||||
getDataStreamMetadata: (dataStreamName: string) => Promise<DataStreamMetadata | null>;
|
||||
|
||||
/**
|
||||
* Marks the given indices as read-only.
|
||||
* First it will roll over the write index if it exists in the deprecated indices.
|
||||
* Then it will unfreeze the indices and set them to read-only.
|
||||
* @param dataStreamName
|
||||
* @param indices
|
||||
*/
|
||||
readonlyIndices: (dataStreamName: string, indices: string[]) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface DataStreamReindexServiceFactoryParams {
|
||||
export interface DataStreamMigrationServiceFactoryParams {
|
||||
esClient: ElasticsearchClient;
|
||||
log: Logger;
|
||||
licensing: LicensingPluginSetup;
|
||||
}
|
||||
|
||||
export const dataStreamReindexServiceFactory = ({
|
||||
export const dataStreamMigrationServiceFactory = ({
|
||||
esClient,
|
||||
licensing,
|
||||
}: DataStreamReindexServiceFactoryParams): DataStreamReindexService => {
|
||||
}: DataStreamMigrationServiceFactoryParams): DataStreamMigrationService => {
|
||||
return {
|
||||
hasRequiredPrivileges: async (dataStreamName: string): Promise<boolean> => {
|
||||
/**
|
||||
|
@ -105,10 +114,15 @@ export const dataStreamReindexServiceFactory = ({
|
|||
|
||||
return resp.has_all_requested;
|
||||
},
|
||||
async detectReindexWarnings(): Promise<DataStreamReindexWarning[]> {
|
||||
async detectMigrationWarnings(): Promise<DataStreamMigrationWarning[]> {
|
||||
return [
|
||||
{
|
||||
warningType: 'affectExistingSetups',
|
||||
resolutionType: 'readonly',
|
||||
},
|
||||
{
|
||||
warningType: 'incompatibleDataStream',
|
||||
resolutionType: 'reindex',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
@ -149,7 +163,7 @@ export const dataStreamReindexServiceFactory = ({
|
|||
);
|
||||
}
|
||||
},
|
||||
async fetchReindexStatus(dataStreamName: string): Promise<DataStreamReindexOperation> {
|
||||
async fetchMigrationStatus(dataStreamName: string): Promise<DataStreamMigrationOperation> {
|
||||
// Check reindexing task progress
|
||||
try {
|
||||
const taskResponse = await esClient.transport.request<DataStreamReindexTaskStatusResponse>({
|
||||
|
@ -169,24 +183,58 @@ export const dataStreamReindexServiceFactory = ({
|
|||
);
|
||||
}
|
||||
|
||||
// Propagate errors from the reindex task even if reindexing is not yet complete.
|
||||
if (taskResponse.errors.length) {
|
||||
// Include the entire task result in the error message. This should be guaranteed
|
||||
// to be JSON-serializable since it just came back from Elasticsearch.
|
||||
throw error.reindexTaskFailed(
|
||||
`Reindexing failed with ${taskResponse.errors.length} errors:\n${JSON.stringify(
|
||||
taskResponse,
|
||||
null,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (taskResponse.complete) {
|
||||
// Check that no failures occurred
|
||||
if (taskResponse.errors.length) {
|
||||
// Include the entire task result in the error message. This should be guaranteed
|
||||
// to be JSON-serializable since it just came back from Elasticsearch.
|
||||
throw error.reindexTaskFailed(
|
||||
`Reindexing failed with ${taskResponse.errors.length} errors:\n${JSON.stringify(
|
||||
taskResponse,
|
||||
null,
|
||||
2
|
||||
)}`
|
||||
/**
|
||||
* If the task is complete, check if there are any remaining indices that require upgrade
|
||||
* If that is the case, we need to update the status to not started
|
||||
* This way the user can trigger a new migration.
|
||||
* Note: This is the best place to do this call because we it'll only be called
|
||||
* 1 timeonce the task is complete.
|
||||
* Cases we reach this code execution:
|
||||
* 1. Task is complete and the user has the UA open. It'll disappear once the user refreshes.
|
||||
* 2. Task is complete but we have remaining indices that require upgrade.
|
||||
*/
|
||||
|
||||
const { data_streams: dataStreamsDeprecations } = await esClient.migration.deprecations({
|
||||
filter_path: `data_streams`,
|
||||
});
|
||||
|
||||
const deprecationsDetails = dataStreamsDeprecations[dataStreamName];
|
||||
if (deprecationsDetails && deprecationsDetails.length) {
|
||||
const deprecationDetails = deprecationsDetails.find(
|
||||
(deprecation) => deprecation._meta!.reindex_required
|
||||
);
|
||||
if (deprecationDetails) {
|
||||
const stillNeedsUpgrade =
|
||||
deprecationDetails._meta!.reindex_required === true &&
|
||||
deprecationDetails._meta!.indices_requiring_upgrade_count > 0;
|
||||
if (stillNeedsUpgrade) {
|
||||
return {
|
||||
status: DataStreamMigrationStatus.notStarted,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the first deprecation that has reindex_required set to true
|
||||
// Update the status
|
||||
return {
|
||||
reindexTaskPercComplete: 1,
|
||||
status: DataStreamReindexStatus.completed,
|
||||
taskPercComplete: 1,
|
||||
status: DataStreamMigrationStatus.completed,
|
||||
resolutionType: 'reindex',
|
||||
progressDetails: {
|
||||
startTimeMs: taskResponse.start_time_millis,
|
||||
successCount: taskResponse.successes,
|
||||
|
@ -200,8 +248,9 @@ export const dataStreamReindexServiceFactory = ({
|
|||
const perc = taskResponse.successes / taskResponse.total_indices_in_data_stream;
|
||||
|
||||
return {
|
||||
status: DataStreamReindexStatus.inProgress,
|
||||
reindexTaskPercComplete: perc,
|
||||
status: DataStreamMigrationStatus.inProgress,
|
||||
taskPercComplete: perc,
|
||||
resolutionType: 'reindex',
|
||||
progressDetails: {
|
||||
startTimeMs: taskResponse.start_time_millis,
|
||||
successCount: taskResponse.successes,
|
||||
|
@ -219,12 +268,13 @@ export const dataStreamReindexServiceFactory = ({
|
|||
// cancelled, never started, or successful task but finished from than 24 hours ago
|
||||
// Since this API should be called as a follow up from _migrate API, we can assume that the task is not started
|
||||
return {
|
||||
status: DataStreamReindexStatus.notStarted,
|
||||
status: DataStreamMigrationStatus.notStarted,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: DataStreamReindexStatus.failed,
|
||||
status: DataStreamMigrationStatus.failed,
|
||||
resolutionType: 'reindex',
|
||||
errorMessage: err.toString(),
|
||||
};
|
||||
}
|
||||
|
@ -240,7 +290,8 @@ export const dataStreamReindexServiceFactory = ({
|
|||
}
|
||||
|
||||
return {
|
||||
status: DataStreamReindexStatus.cancelled,
|
||||
status: DataStreamMigrationStatus.cancelled,
|
||||
resolutionType: 'reindex',
|
||||
};
|
||||
},
|
||||
async getDataStreamMetadata(dataStreamName: string): Promise<DataStreamMetadata | null> {
|
||||
|
@ -285,9 +336,9 @@ export const dataStreamReindexServiceFactory = ({
|
|||
throw error.cannotGrabMetadata(`Index ${index} does not exist in this cluster.`);
|
||||
}
|
||||
|
||||
indicesRequiringUpgradeDocsSize += (indexStats[1] as any).total.store
|
||||
indicesRequiringUpgradeDocsSize += (indexStats[1] as any).primaries.store
|
||||
.total_data_set_size_in_bytes;
|
||||
indicesRequiringUpgradeDocsCount += (indexStats[1] as any).total.docs.count;
|
||||
indicesRequiringUpgradeDocsCount += (indexStats[1] as any).primaries.docs.count;
|
||||
|
||||
const body = await esClient.indices.getSettings({
|
||||
index,
|
||||
|
@ -319,5 +370,49 @@ export const dataStreamReindexServiceFactory = ({
|
|||
);
|
||||
}
|
||||
},
|
||||
|
||||
async readonlyIndices(dataStreamName: string, indices: string[]) {
|
||||
try {
|
||||
const { data_streams: dataStreamsDetails } = await esClient.indices.getDataStream({
|
||||
name: dataStreamName,
|
||||
});
|
||||
// Since we are not using a pattern it should only return one item
|
||||
const dataStreamBackIndices = dataStreamsDetails[0].indices;
|
||||
|
||||
// The last item in this array contains information about the stream’s current write index.
|
||||
const writeIndex = dataStreamBackIndices[dataStreamBackIndices.length - 1].index_name;
|
||||
const hasWriteIndex = indices.some((index) => index === writeIndex);
|
||||
|
||||
if (hasWriteIndex) {
|
||||
const rollOverResponse = await esClient.indices.rollover({
|
||||
alias: dataStreamName,
|
||||
});
|
||||
if (!rollOverResponse.acknowledged) {
|
||||
throw error.readonlyTaskFailed(`Could not rollover data stream ${dataStreamName}.`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
throw error.readonlyTaskFailed(`Could not migrate data stream ${dataStreamName}.`);
|
||||
}
|
||||
|
||||
for (const index of indices) {
|
||||
try {
|
||||
const addBlock = await esClient.indices.addBlock({ index, block: 'write' });
|
||||
|
||||
if (!addBlock.acknowledged) {
|
||||
throw error.readonlyTaskFailed(`Could not set index ${index} to readonly.`);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof DataStreamMigrationError) {
|
||||
throw err;
|
||||
}
|
||||
// ES errors are serializable, so we can just stringify the error and throw it.
|
||||
const stringifiedErr = JSON.stringify(err, null, 2);
|
||||
throw error.readonlyTaskFailed(
|
||||
`Could not migrate index "${index}". Got: ${stringifiedErr}`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
|
@ -12,16 +12,17 @@ import {
|
|||
ReindexAlreadyInProgress,
|
||||
ReindexCannotBeCancelled,
|
||||
MetadataCannotBeGrabbed,
|
||||
ReadonlyTaskFailed,
|
||||
} from './error_symbols';
|
||||
|
||||
export class ReindexError extends Error {
|
||||
export class DataStreamMigrationError extends Error {
|
||||
constructor(message: string, public readonly symbol: symbol) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export const createErrorFactory = (symbol: symbol) => (message: string) => {
|
||||
return new ReindexError(message, symbol);
|
||||
return new DataStreamMigrationError(message, symbol);
|
||||
};
|
||||
|
||||
export const error = {
|
||||
|
@ -31,4 +32,5 @@ export const error = {
|
|||
reindexTaskFailed: createErrorFactory(ReindexTaskFailed),
|
||||
reindexAlreadyInProgress: createErrorFactory(ReindexAlreadyInProgress),
|
||||
reindexCannotBeCancelled: createErrorFactory(ReindexCannotBeCancelled),
|
||||
readonlyTaskFailed: createErrorFactory(ReadonlyTaskFailed),
|
||||
};
|
||||
|
|
|
@ -11,3 +11,4 @@ export const ReindexTaskFailed = Symbol('ReindexTaskFailed');
|
|||
export const ReindexAlreadyInProgress = Symbol('ReindexAlreadyInProgress');
|
||||
export const ReindexCannotBeCancelled = Symbol('ReindexCannotBeCancelled');
|
||||
export const MetadataCannotBeGrabbed = Symbol('MetadataCannotBeGrabbed');
|
||||
export const ReadonlyTaskFailed = Symbol('ReadonlyTaskFailed');
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { dataStreamReindexServiceFactory } from './data_stream_reindex_service';
|
||||
export { dataStreamMigrationServiceFactory } from './data_stream_migration_service';
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 { DeprecationDetailsMessage, DeprecationsDetails } from '@kbn/core-deprecations-common';
|
||||
import { GetDeprecationsContext } from '@kbn/core-deprecations-server';
|
||||
|
||||
import { getEnterpriseSearchPre8IndexDeprecations } from './enterprise_search_deprecations';
|
||||
import indexDeprecatorFxns = require('./pre_eight_index_deprecator');
|
||||
|
||||
const ctx = {
|
||||
esClient: {
|
||||
asInternalUser: {},
|
||||
},
|
||||
} as GetDeprecationsContext;
|
||||
|
||||
function getMessageFromDeprecation(details: DeprecationsDetails): string {
|
||||
const message = details.message as DeprecationDetailsMessage;
|
||||
return message.content;
|
||||
}
|
||||
|
||||
describe('getEnterpriseSearchPre8IndexDeprecations', () => {
|
||||
it('can register index and data stream deprecations that need to be set to read only', async () => {
|
||||
const getIndicesMock = jest.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: '.ent-search-index_without_datastream',
|
||||
hasDatastream: false,
|
||||
datastreams: [],
|
||||
},
|
||||
{
|
||||
name: '.ent-search-with_data_stream',
|
||||
hasDatastream: true,
|
||||
datastreams: ['datastream-testing'],
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
jest
|
||||
.spyOn(indexDeprecatorFxns, 'getPreEightEnterpriseSearchIndices')
|
||||
.mockImplementation(getIndicesMock);
|
||||
|
||||
const deprecations = await getEnterpriseSearchPre8IndexDeprecations(ctx, 'docsurl');
|
||||
expect(deprecations).toHaveLength(1);
|
||||
expect(deprecations[0].correctiveActions.api?.path).toStrictEqual(
|
||||
'/internal/enterprise_search/deprecations/set_enterprise_search_indices_read_only'
|
||||
);
|
||||
expect(deprecations[0].title).toMatch('Pre 8.x Enterprise Search indices compatibility');
|
||||
expect(getMessageFromDeprecation(deprecations[0])).toContain(
|
||||
'The following indices are found to be incompatible for upgrade'
|
||||
);
|
||||
expect(getMessageFromDeprecation(deprecations[0])).toContain(
|
||||
'.ent-search-index_without_datastream'
|
||||
);
|
||||
expect(getMessageFromDeprecation(deprecations[0])).toContain(
|
||||
'The following data streams are found to be incompatible for upgrade'
|
||||
);
|
||||
expect(getMessageFromDeprecation(deprecations[0])).toContain('.ent-search-with_data_stream');
|
||||
});
|
||||
|
||||
it('can register an index without data stream deprecations that need to be set to read only', async () => {
|
||||
const getIndicesMock = jest.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: '.ent-search-index_without_datastream',
|
||||
hasDatastream: false,
|
||||
datastreams: [''],
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
jest
|
||||
.spyOn(indexDeprecatorFxns, 'getPreEightEnterpriseSearchIndices')
|
||||
.mockImplementation(getIndicesMock);
|
||||
|
||||
const deprecations = await getEnterpriseSearchPre8IndexDeprecations(ctx, 'docsurl');
|
||||
expect(deprecations).toHaveLength(1);
|
||||
expect(deprecations[0].correctiveActions.api?.path).toStrictEqual(
|
||||
'/internal/enterprise_search/deprecations/set_enterprise_search_indices_read_only'
|
||||
);
|
||||
expect(deprecations[0].title).toMatch('Pre 8.x Enterprise Search indices compatibility');
|
||||
expect(getMessageFromDeprecation(deprecations[0])).toContain(
|
||||
'The following indices are found to be incompatible for upgrade'
|
||||
);
|
||||
expect(getMessageFromDeprecation(deprecations[0])).toContain(
|
||||
'.ent-search-index_without_datastream'
|
||||
);
|
||||
expect(getMessageFromDeprecation(deprecations[0])).not.toContain(
|
||||
'The following data streams are found to be incompatible for upgrade'
|
||||
);
|
||||
expect(getMessageFromDeprecation(deprecations[0])).not.toContain(
|
||||
'.ent-search-with_data_stream'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 { DeprecationsDetails } from '@kbn/core-deprecations-common';
|
||||
import { GetDeprecationsContext, RegisterDeprecationsConfig } from '@kbn/core-deprecations-server';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getPreEightEnterpriseSearchIndices } from './pre_eight_index_deprecator';
|
||||
|
||||
export const getEntepriseSearchRegisteredDeprecations = (
|
||||
docsUrl: string
|
||||
): RegisterDeprecationsConfig => {
|
||||
return {
|
||||
getDeprecations: async (ctx: GetDeprecationsContext) => {
|
||||
const [entSearchIndexIncompatibility] = await Promise.all([
|
||||
getEnterpriseSearchPre8IndexDeprecations(ctx, docsUrl),
|
||||
]);
|
||||
return [...entSearchIndexIncompatibility];
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* If there are any Enterprise Search indices that were created with Elasticsearch 7.x, they must be removed
|
||||
* or set to read-only
|
||||
*/
|
||||
export async function getEnterpriseSearchPre8IndexDeprecations(
|
||||
ctx: GetDeprecationsContext,
|
||||
docsUrl: string
|
||||
): Promise<DeprecationsDetails[]> {
|
||||
const deprecations: DeprecationsDetails[] = [];
|
||||
|
||||
const entSearchIndices = await getPreEightEnterpriseSearchIndices(ctx.esClient.asInternalUser);
|
||||
if (!entSearchIndices || entSearchIndices.length === 0) {
|
||||
return deprecations;
|
||||
}
|
||||
|
||||
let indicesList = '';
|
||||
let datastreamsList = '';
|
||||
for (const index of entSearchIndices) {
|
||||
if (index.hasDatastream) {
|
||||
indicesList += `${index.name}\n`;
|
||||
for (const datastream of index.datastreams) {
|
||||
if (datastream === '') continue;
|
||||
datastreamsList += `${datastream}\n`;
|
||||
}
|
||||
} else {
|
||||
indicesList += `${index.name}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
let message = `There are ${entSearchIndices.length} incompatible Enterprise Search indices.\n\n`;
|
||||
|
||||
if (indicesList.length > 0) {
|
||||
message +=
|
||||
'The following indices are found to be incompatible for upgrade:\n\n' +
|
||||
'```\n' +
|
||||
`${indicesList}` +
|
||||
'\n```\n' +
|
||||
'These indices must be either set to read-only or deleted before upgrading.\n';
|
||||
}
|
||||
|
||||
if (datastreamsList.length > 0) {
|
||||
message +=
|
||||
'\nThe following data streams are found to be incompatible for upgrade:\n\n' +
|
||||
'```\n' +
|
||||
`${datastreamsList}` +
|
||||
'\n```\n' +
|
||||
'Using the "quick resolve" button below will roll over any datastreams and set all incompatible indices to read-only.\n\n' +
|
||||
'Alternatively, manually deleting these indices and data streams will also unblock your upgrade.';
|
||||
} else {
|
||||
message +=
|
||||
'Setting these indices to read-only can be attempted with the "quick resolve" button below.\n\n' +
|
||||
'Alternatively, manually deleting these indices will also unblock your upgrade.';
|
||||
}
|
||||
|
||||
deprecations.push({
|
||||
level: 'critical',
|
||||
deprecationType: 'feature',
|
||||
title: i18n.translate(
|
||||
'xpack.upgradeAssistant.deprecations.incompatibleEnterpriseSearchIndexes.title',
|
||||
{
|
||||
defaultMessage: 'Pre 8.x Enterprise Search indices compatibility',
|
||||
}
|
||||
),
|
||||
message: {
|
||||
type: 'markdown',
|
||||
content: i18n.translate(
|
||||
'xpack.upgradeAssistant.deprecations.incompatibleEnterpriseSearchIndexes.message',
|
||||
{
|
||||
defaultMessage: message,
|
||||
}
|
||||
),
|
||||
},
|
||||
documentationUrl: docsUrl,
|
||||
correctiveActions: {
|
||||
manualSteps: [
|
||||
i18n.translate(
|
||||
'xpack.upgradeAssistant.deprecations.incompatibleEnterpriseSearchIndexes.deleteIndices',
|
||||
{
|
||||
defaultMessage: 'Set all incompatible indices and data streams to read only, or',
|
||||
}
|
||||
),
|
||||
i18n.translate(
|
||||
'xpack.upgradeAssistant.deprecations.incompatibleEnterpriseSearchIndexes.deleteIndices',
|
||||
{
|
||||
defaultMessage: 'Delete all incompatible indices and data streams',
|
||||
}
|
||||
),
|
||||
],
|
||||
api: {
|
||||
method: 'POST',
|
||||
path: '/internal/enterprise_search/deprecations/set_enterprise_search_indices_read_only',
|
||||
body: {
|
||||
deprecationDetails: { domainId: 'enterpriseSearch' },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return deprecations;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 { kibanaResponseFactory } from '@kbn/core/server';
|
||||
|
||||
import { handleEsError } from '../../shared_imports';
|
||||
import {
|
||||
createMockRouter,
|
||||
MockRouter,
|
||||
routeHandlerContextMock,
|
||||
} from '../../routes/__mocks__/routes.mock';
|
||||
import { createRequestMock } from '../../routes/__mocks__/request.mock';
|
||||
|
||||
jest.mock('../es_version_precheck', () => ({
|
||||
versionCheckHandlerWrapper: (a: any) => a,
|
||||
}));
|
||||
|
||||
import indexDeprecatorFxns = require('./pre_eight_index_deprecator');
|
||||
|
||||
import { registerEnterpriseSearchDeprecationRoutes } from './enterprise_search_deprecations_routes';
|
||||
|
||||
describe('deprecation routes', () => {
|
||||
let routeDependencies: any;
|
||||
|
||||
describe('POST /internal/enterprise_search/deprecations/set_enterprise_search_indices_read_only', () => {
|
||||
let mockRouter: MockRouter;
|
||||
|
||||
function registerMockRouter({ mlSnapshots } = { mlSnapshots: true }) {
|
||||
mockRouter = createMockRouter();
|
||||
routeDependencies = {
|
||||
config: {
|
||||
featureSet: { mlSnapshots, migrateSystemIndices: true, reindexCorrectiveActions: true },
|
||||
},
|
||||
router: mockRouter,
|
||||
lib: { handleEsError },
|
||||
};
|
||||
registerEnterpriseSearchDeprecationRoutes(routeDependencies);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
registerMockRouter();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('sets read-only and 200s correctly in happy path', async () => {
|
||||
const setIndicesReadOnlyMock = jest.spyOn(
|
||||
indexDeprecatorFxns,
|
||||
'setPreEightEnterpriseSearchIndicesReadOnly'
|
||||
);
|
||||
|
||||
setIndicesReadOnlyMock.mockResolvedValue('');
|
||||
|
||||
const resp = await routeDependencies.router.getHandler({
|
||||
method: 'post',
|
||||
pathPattern:
|
||||
'/internal/enterprise_search/deprecations/set_enterprise_search_indices_read_only',
|
||||
})(
|
||||
routeHandlerContextMock,
|
||||
createRequestMock({
|
||||
body: { deprecationDetails: { domainId: 'enterpriseSearch' } },
|
||||
}),
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(resp.status).toEqual(200);
|
||||
expect(resp.payload).toEqual({
|
||||
acknowedged: true,
|
||||
});
|
||||
|
||||
expect(setIndicesReadOnlyMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { setPreEightEnterpriseSearchIndicesReadOnly } from './pre_eight_index_deprecator';
|
||||
import { versionCheckHandlerWrapper } from '../es_version_precheck';
|
||||
import { RouteDependencies } from '../../types';
|
||||
|
||||
export function registerEnterpriseSearchDeprecationRoutes({ router }: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
path: '/internal/enterprise_search/deprecations/set_enterprise_search_indices_read_only',
|
||||
validate: {},
|
||||
},
|
||||
versionCheckHandlerWrapper(async ({ core }, request, response) => {
|
||||
const { client } = (await core).elasticsearch;
|
||||
const setResponse = await setPreEightEnterpriseSearchIndicesReadOnly(client.asCurrentUser);
|
||||
if (setResponse.length > 0) {
|
||||
return response.badRequest({
|
||||
body: { message: setResponse },
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
}
|
||||
return response.ok({
|
||||
body: { acknowedged: true },
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
ENT_SEARCH_DATASTREAM_PATTERN,
|
||||
ENT_SEARCH_DATASTREAM_PREFIXES,
|
||||
ENT_SEARCH_INDEX_PREFIX,
|
||||
} from './pre_eight_index_deprecator';
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* 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 { ElasticsearchClient } from '@kbn/core/server';
|
||||
|
||||
import {
|
||||
getPreEightEnterpriseSearchIndices,
|
||||
setPreEightEnterpriseSearchIndicesReadOnly,
|
||||
} from './pre_eight_index_deprecator';
|
||||
import type {
|
||||
IndicesDataStream,
|
||||
IndicesGetDataStreamResponse,
|
||||
IndicesGetResponse,
|
||||
IndicesIndexState,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
|
||||
const testIndices = {
|
||||
'.ent-search-already_read_only': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
blocks: {
|
||||
write: 'true',
|
||||
},
|
||||
verified_read_only: 'true',
|
||||
},
|
||||
},
|
||||
data_stream: 'datastream-123',
|
||||
},
|
||||
'.ent-search-post_7_index': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '8.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'.ent-search-index_without_datastream': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'.ent-search-with_data_stream': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
data_stream: 'datastream-testing',
|
||||
},
|
||||
'.ent-search-with_another_data_stream': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
data_stream: 'datastream-testing-another',
|
||||
},
|
||||
'.ent-search-with_same_data_stream': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
data_stream: 'datastream-testing',
|
||||
},
|
||||
};
|
||||
|
||||
const testBackingIndex = {
|
||||
'.ds-some-other-backing-index': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
data_stream: 'logs-app_search.testdatastream',
|
||||
},
|
||||
};
|
||||
|
||||
const additionalDatastreams: Record<string, IndicesDataStream> = {
|
||||
'logs-app_search.testdatastream': {
|
||||
name: 'logs-app_search.testdatastream',
|
||||
indices: [
|
||||
{ index_name: '.ds-some-other-backing-index', index_uuid: '1' },
|
||||
{ index_name: '.ent-search-with_same_data_stream', index_uuid: '2' },
|
||||
],
|
||||
} as IndicesDataStream,
|
||||
};
|
||||
|
||||
const testIndicesWithoutDatastream: Record<string, IndicesIndexState> = {
|
||||
'.ent-search-already_read_only': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
blocks: {
|
||||
write: 'true',
|
||||
},
|
||||
verified_read_only: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
'.ent-search-post_7_index': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '8.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'.ent-search-index_without_datastream': {
|
||||
settings: {
|
||||
index: {
|
||||
version: {
|
||||
created: '7.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function getMockIndicesFxn(values: Record<string, IndicesIndexState>) {
|
||||
return () => {
|
||||
const ret: IndicesGetResponse = {};
|
||||
for (const [index, indexData] of Object.entries(values)) {
|
||||
ret[index] = indexData;
|
||||
}
|
||||
return Promise.resolve(ret);
|
||||
};
|
||||
}
|
||||
|
||||
function getMockDatastreamsFxn(values: Record<string, IndicesDataStream>) {
|
||||
return () => {
|
||||
const ret: IndicesGetDataStreamResponse = { data_streams: [] };
|
||||
for (const [, datastreamData] of Object.entries(values)) {
|
||||
ret.data_streams.push(datastreamData);
|
||||
}
|
||||
return Promise.resolve(ret);
|
||||
};
|
||||
}
|
||||
|
||||
describe('getPreEightEnterpriseSearchIndices', () => {
|
||||
let esClientMock: ElasticsearchClient;
|
||||
let getIndicesMock: jest.Mock;
|
||||
let getDatastreamsMock: jest.Mock;
|
||||
beforeEach(() => {
|
||||
getIndicesMock = jest.fn();
|
||||
getIndicesMock.mockImplementationOnce(getMockIndicesFxn(testIndices));
|
||||
getIndicesMock.mockImplementationOnce(getMockIndicesFxn(testBackingIndex));
|
||||
|
||||
getDatastreamsMock = jest.fn(getMockDatastreamsFxn(additionalDatastreams));
|
||||
esClientMock = {
|
||||
indices: {
|
||||
get: getIndicesMock,
|
||||
getDataStream: getDatastreamsMock,
|
||||
},
|
||||
} as unknown as ElasticsearchClient;
|
||||
});
|
||||
|
||||
it('returns the correct indices', async () => {
|
||||
const indices = await getPreEightEnterpriseSearchIndices(esClientMock);
|
||||
expect(indices).toEqual([
|
||||
{
|
||||
name: '.ent-search-index_without_datastream',
|
||||
hasDatastream: false,
|
||||
datastreams: [''],
|
||||
},
|
||||
{
|
||||
name: '.ent-search-with_data_stream',
|
||||
hasDatastream: true,
|
||||
datastreams: ['datastream-testing'],
|
||||
},
|
||||
{
|
||||
name: '.ent-search-with_another_data_stream',
|
||||
hasDatastream: true,
|
||||
datastreams: ['datastream-testing-another'],
|
||||
},
|
||||
{
|
||||
name: '.ent-search-with_same_data_stream',
|
||||
hasDatastream: true,
|
||||
datastreams: ['datastream-testing'],
|
||||
},
|
||||
{
|
||||
name: '.ds-some-other-backing-index',
|
||||
hasDatastream: true,
|
||||
datastreams: ['logs-app_search.testdatastream'],
|
||||
},
|
||||
]);
|
||||
expect(getIndicesMock).toHaveBeenCalledTimes(2);
|
||||
expect(getIndicesMock).toHaveBeenNthCalledWith(1, {
|
||||
expand_wildcards: ['all', 'hidden'],
|
||||
ignore_unavailable: true,
|
||||
index: '.ent-search-*',
|
||||
});
|
||||
expect(getIndicesMock).toHaveBeenNthCalledWith(2, {
|
||||
ignore_unavailable: true,
|
||||
index: ['.ds-some-other-backing-index'],
|
||||
});
|
||||
|
||||
expect(getDatastreamsMock).toHaveBeenCalledTimes(1);
|
||||
expect(getDatastreamsMock).toHaveBeenCalledWith({
|
||||
expand_wildcards: ['all', 'hidden'],
|
||||
name: 'logs-enterprise_search.*,logs-app_search.*,logs-workplace_search.*',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setPreEightEnterpriseSearchIndicesReadOnly', () => {
|
||||
it('does not rollover datastreams if there are none', async () => {
|
||||
const getIndicesMock = jest.fn(getMockIndicesFxn(testIndicesWithoutDatastream));
|
||||
const getDatastreamsMock = jest.fn(() => Promise.resolve({ data_streams: [] }));
|
||||
const rolloverMock = jest.fn(() => Promise.resolve(true));
|
||||
const addBlockMock = jest.fn(() => Promise.resolve({ acknowledged: true }));
|
||||
const esClientMock = {
|
||||
indices: {
|
||||
get: getIndicesMock,
|
||||
getDataStream: getDatastreamsMock,
|
||||
rollover: rolloverMock,
|
||||
addBlock: addBlockMock,
|
||||
},
|
||||
} as unknown as ElasticsearchClient;
|
||||
|
||||
const result = await setPreEightEnterpriseSearchIndicesReadOnly(esClientMock);
|
||||
expect(result).toEqual('');
|
||||
expect(getIndicesMock).toHaveBeenCalledTimes(1);
|
||||
expect(rolloverMock).not.toHaveBeenCalled();
|
||||
expect(addBlockMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does rollover datastreams if there are any', async () => {
|
||||
const getIndicesMock = jest.fn();
|
||||
getIndicesMock.mockImplementationOnce(getMockIndicesFxn(testIndices));
|
||||
getIndicesMock.mockImplementationOnce(getMockIndicesFxn(testBackingIndex));
|
||||
getIndicesMock.mockImplementationOnce(getMockIndicesFxn(testIndices));
|
||||
getIndicesMock.mockImplementationOnce(getMockIndicesFxn(testBackingIndex));
|
||||
|
||||
const getDatastreamsMock = getMockDatastreamsFxn(additionalDatastreams);
|
||||
const rolloverMock = jest.fn(() => Promise.resolve(true));
|
||||
const addBlockMock = jest.fn(() => Promise.resolve({ acknowledged: true }));
|
||||
const esClientMock = {
|
||||
indices: {
|
||||
get: getIndicesMock,
|
||||
getDataStream: getDatastreamsMock,
|
||||
rollover: rolloverMock,
|
||||
addBlock: addBlockMock,
|
||||
},
|
||||
} as unknown as ElasticsearchClient;
|
||||
|
||||
const result = await setPreEightEnterpriseSearchIndicesReadOnly(esClientMock);
|
||||
expect(result).toEqual('');
|
||||
expect(getIndicesMock).toHaveBeenCalledTimes(4);
|
||||
expect(rolloverMock).toHaveBeenCalledTimes(3);
|
||||
expect(addBlockMock).toHaveBeenCalledTimes(5);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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 { IndicesIndexState } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { ElasticsearchClient } from '@kbn/core/server';
|
||||
|
||||
export const ENT_SEARCH_INDEX_PREFIX = '.ent-search-';
|
||||
export const ENT_SEARCH_DATASTREAM_PREFIXES = [
|
||||
'logs-enterprise_search.',
|
||||
'logs-app_search.',
|
||||
'logs-workplace_search.',
|
||||
];
|
||||
export const ENT_SEARCH_DATASTREAM_PATTERN = [
|
||||
'logs-enterprise_search.*',
|
||||
'logs-app_search.*',
|
||||
'logs-workplace_search.*',
|
||||
];
|
||||
|
||||
export interface EnterpriseSearchIndexMapping {
|
||||
name: string;
|
||||
hasDatastream: boolean;
|
||||
datastreams: string[];
|
||||
}
|
||||
|
||||
function is7xIncompatibleIndex(indexData: IndicesIndexState): boolean {
|
||||
const isReadOnly = indexData.settings?.index?.verified_read_only ?? 'false';
|
||||
return Boolean(
|
||||
indexData.settings?.index?.version?.created?.startsWith('7') && isReadOnly !== 'true'
|
||||
);
|
||||
}
|
||||
|
||||
export async function getPreEightEnterpriseSearchIndices(
|
||||
esClient: ElasticsearchClient
|
||||
): Promise<EnterpriseSearchIndexMapping[]> {
|
||||
const entSearchIndices = await esClient.indices.get({
|
||||
index: `${ENT_SEARCH_INDEX_PREFIX}*`,
|
||||
ignore_unavailable: true,
|
||||
expand_wildcards: ['all', 'hidden'],
|
||||
});
|
||||
|
||||
const returnIndices: EnterpriseSearchIndexMapping[] = [];
|
||||
|
||||
for (const [index, indexData] of Object.entries(entSearchIndices)) {
|
||||
if (is7xIncompatibleIndex(indexData)) {
|
||||
const dataStreamName = indexData.data_stream;
|
||||
returnIndices.push({
|
||||
name: index,
|
||||
hasDatastream: dataStreamName ? true : false,
|
||||
datastreams: [dataStreamName ?? ''],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { data_streams: entSearchDatastreams } = await esClient.indices.getDataStream({
|
||||
name: ENT_SEARCH_DATASTREAM_PATTERN.join(','),
|
||||
expand_wildcards: ['all', 'hidden'],
|
||||
});
|
||||
|
||||
const dsIndices = new Set<string>();
|
||||
entSearchDatastreams.forEach(({ indices: dsi }) => {
|
||||
dsi.forEach(({ index_name: indexName }) => {
|
||||
dsIndices.add(indexName);
|
||||
});
|
||||
});
|
||||
|
||||
if (!dsIndices.size) return returnIndices;
|
||||
|
||||
for (const returnIndex of returnIndices) {
|
||||
if (dsIndices.has(returnIndex.name)) {
|
||||
dsIndices.delete(returnIndex.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dsIndices.size) return returnIndices;
|
||||
|
||||
const entSearchDsIndices = await esClient.indices.get({
|
||||
index: Array.from(dsIndices.values()),
|
||||
ignore_unavailable: true,
|
||||
});
|
||||
|
||||
for (const [index, indexData] of Object.entries(entSearchDsIndices)) {
|
||||
if (is7xIncompatibleIndex(indexData)) {
|
||||
const dataStreamName = indexData.data_stream;
|
||||
returnIndices.push({
|
||||
name: index,
|
||||
hasDatastream: dataStreamName ? true : false,
|
||||
datastreams: [dataStreamName ?? ''],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return returnIndices;
|
||||
}
|
||||
|
||||
export async function setPreEightEnterpriseSearchIndicesReadOnly(
|
||||
esClient: ElasticsearchClient
|
||||
): Promise<string> {
|
||||
// get the indices again to ensure nothing's changed since the last check
|
||||
let indices = await getPreEightEnterpriseSearchIndices(esClient);
|
||||
|
||||
// rollover any datastreams first
|
||||
const rolledOverDatastreams: { [id: string]: boolean } = {};
|
||||
for (const index of indices) {
|
||||
if (index.hasDatastream) {
|
||||
for (const datastream of index.datastreams) {
|
||||
if (datastream.length === 0 || rolledOverDatastreams[datastream]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const indexResponse = await esClient.indices.rollover({ alias: datastream });
|
||||
|
||||
if (!indexResponse) {
|
||||
return `Could not roll over datastream: ${index.name}`;
|
||||
}
|
||||
|
||||
rolledOverDatastreams[datastream] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(rolledOverDatastreams).length > 0) {
|
||||
// we rolled over at least one datastream,
|
||||
// get the indices again
|
||||
indices = await getPreEightEnterpriseSearchIndices(esClient);
|
||||
}
|
||||
|
||||
for (const index of indices) {
|
||||
const indexName = index.name;
|
||||
const indexResponse = await esClient.indices.addBlock({ index: indexName, block: 'write' });
|
||||
|
||||
if (!indexResponse || indexResponse.acknowledged !== true) {
|
||||
return `Could not set index read-only: ${indexName}`;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
|
@ -80,7 +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",
|
||||
|
@ -93,21 +98,29 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": undefined,
|
||||
"type": "reindex",
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": true,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "unfreeze",
|
||||
},
|
||||
"details": "This index has version: 7.17.28-8.0.0",
|
||||
"frozen": true,
|
||||
"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",
|
||||
|
@ -175,7 +188,33 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"blockerForReindexing": undefined,
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"transformIds": Array [
|
||||
"abc",
|
||||
],
|
||||
"type": "reindex",
|
||||
},
|
||||
"details": "This index has version: 7.17.25",
|
||||
"index": "transforms_index",
|
||||
"isCritical": true,
|
||||
"message": "Old index with a compatibility version < 8.0",
|
||||
"resolveDuringUpgrade": false,
|
||||
"type": "index_settings",
|
||||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
},
|
||||
Object {
|
||||
"correctiveAction": Object {
|
||||
"excludedActions": Array [],
|
||||
"metadata": Object {
|
||||
"isClosedIndex": false,
|
||||
"isFrozenIndex": false,
|
||||
"isInDataStream": false,
|
||||
},
|
||||
"type": "reindex",
|
||||
},
|
||||
"details": "This index has version: 7.17.25",
|
||||
|
@ -187,7 +226,20 @@ Object {
|
|||
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
|
||||
},
|
||||
Object {
|
||||
"correctiveAction": undefined,
|
||||
"correctiveAction": Object {
|
||||
"metadata": Object {
|
||||
"excludedActions": Array [],
|
||||
"ignoredIndicesRequiringUpgrade": Array [],
|
||||
"ignoredIndicesRequiringUpgradeCount": 0,
|
||||
"indicesRequiringUpgrade": Array [
|
||||
".ds-some-backing-index-5-2024.11.07-000001",
|
||||
],
|
||||
"indicesRequiringUpgradeCount": 1,
|
||||
"reindexRequired": true,
|
||||
"totalBackingIndices": 2,
|
||||
},
|
||||
"type": "dataStream",
|
||||
},
|
||||
"details": "This data stream has backing indices that were created before Elasticsearch 8.0.0",
|
||||
"index": "my-v7-data-stream",
|
||||
"isCritical": true,
|
||||
|
@ -217,7 +269,7 @@ Object {
|
|||
"url": "https://github.com/elastic/elasticsearch/pull/117172",
|
||||
},
|
||||
],
|
||||
"totalCriticalDeprecations": 8,
|
||||
"totalCriticalDeprecations": 9,
|
||||
"totalCriticalHealthIssues": 0,
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -5,24 +5,31 @@
|
|||
* 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 DataStreamActionMetadata {
|
||||
actions?: Action[];
|
||||
|
||||
interface IndexActionMetadata extends CommonActionMetadata {
|
||||
reindex_required: boolean;
|
||||
transform_ids: string[];
|
||||
is_in_data_stream?: boolean;
|
||||
}
|
||||
|
||||
interface DataStreamActionMetadata extends CommonActionMetadata {
|
||||
excludedActions?: Array<'readOnly' | 'reindex'>;
|
||||
total_backing_indices: number;
|
||||
reindex_required: boolean;
|
||||
|
||||
|
@ -35,33 +42,28 @@ 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_REQIURING_REINDEX = [
|
||||
/Index created before/,
|
||||
/index with a compatibility version \</,
|
||||
];
|
||||
export const isFrozenDeprecation = (message: string, indexName?: string): boolean =>
|
||||
Boolean(indexName) && message.includes(`Index [${indexName}] is a frozen index`);
|
||||
|
||||
export const getCorrectiveAction = (deprecation: BaseDeprecation): CorrectiveAction | undefined => {
|
||||
const { index, type, message, metadata } = deprecation;
|
||||
|
||||
export const getCorrectiveAction = (
|
||||
deprecationType: EnrichedDeprecationInfo['type'],
|
||||
message: string,
|
||||
metadata: EsMetadata,
|
||||
indexName?: string
|
||||
): EnrichedDeprecationInfo['correctiveAction'] => {
|
||||
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'
|
||||
);
|
||||
const requiresReindexAction = ES_INDEX_MESSAGES_REQIURING_REINDEX.some((regexp) =>
|
||||
regexp.test(message)
|
||||
(action) => action.action_type === 'remove_settings' && typeof index === 'undefined'
|
||||
);
|
||||
const requiresReindexAction =
|
||||
(type === 'index_settings' || type === 'node_settings') &&
|
||||
(deprecation.metadata as IndexActionMetadata)?.reindex_required === true;
|
||||
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 {
|
||||
|
@ -97,8 +99,26 @@ export const getCorrectiveAction = (
|
|||
}
|
||||
|
||||
if (requiresReindexAction) {
|
||||
const transformIds = (metadata as IndexActionMetadata)?.transform_ids;
|
||||
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,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue