From c77c8ab2abe0364dc0a286ea44cb4b203d5721eb Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Thu, 7 Oct 2021 13:49:03 +0200 Subject: [PATCH] [Upgrade Assistant] Added reindexing progress in % to the reindex flyout and es deprecations table --- .../reindex_deprecation_flyout.test.ts | 107 +++++++++++++++ .../checklist_step.test.tsx.snap | 11 -- .../reindex/flyout/checklist_step.tsx | 9 -- .../reindex/flyout/progress.test.tsx | 125 +++++++++--------- .../reindex/flyout/progress.tsx | 98 ++++++++------ .../reindex/resolution_table_cell.tsx | 9 +- .../reindex/use_reindex_state.tsx | 4 +- .../public/application/lib/utils.test.ts | 33 ++++- .../public/application/lib/utils.ts | 50 +++++++ 9 files changed, 323 insertions(+), 123 deletions(-) diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts index ca9253c1140b0..3c6fe0e5f5329 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts @@ -10,6 +10,7 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment } from '../helpers'; import { ElasticsearchTestBed, setupElasticsearchPage } from './es_deprecations.helpers'; import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; +import { ReindexStatus, ReindexStep } from '../../../common/types'; // Note: The reindexing flyout UX is subject to change; more tests should be added here once functionality is built out describe('Reindex deprecation flyout', () => { @@ -93,4 +94,110 @@ describe('Reindex deprecation flyout', () => { 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({ + reindexOp: { + status: ReindexStatus.inProgress, + lastCompletedStep: ReindexStep.readonly, + reindexTaskPercComplete: null, + }, + warnings: [], + hasRequiredPrivileges: true, + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + const { actions, find, exists } = testBed; + + await actions.table.clickDeprecationRowAt('reindex', 0); + + expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 5%'); + expect(exists('cancelReindexingDocumentsButton')).toBe(false); + }); + + it('has started reindexing documents', async () => { + httpRequestsMockHelpers.setReindexStatusResponse({ + reindexOp: { + status: ReindexStatus.inProgress, + lastCompletedStep: ReindexStep.reindexStarted, + reindexTaskPercComplete: 0.25, + }, + warnings: [], + hasRequiredPrivileges: true, + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + const { actions, find, exists } = testBed; + + await actions.table.clickDeprecationRowAt('reindex', 0); + + expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 31%'); + expect(exists('cancelReindexingDocumentsButton')).toBe(true); + }); + + it('has completed reindexing documents', async () => { + httpRequestsMockHelpers.setReindexStatusResponse({ + reindexOp: { + status: ReindexStatus.inProgress, + lastCompletedStep: ReindexStep.reindexCompleted, + reindexTaskPercComplete: 1, + }, + warnings: [], + hasRequiredPrivileges: true, + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + const { actions, find, exists } = testBed; + + await actions.table.clickDeprecationRowAt('reindex', 0); + + expect(find('reindexChecklistTitle').text()).toEqual('Reindexing in progress… 95%'); + expect(exists('cancelReindexingDocumentsButton')).toBe(false); + }); + + it('has completed', async () => { + httpRequestsMockHelpers.setReindexStatusResponse({ + reindexOp: { + status: ReindexStatus.completed, + lastCompletedStep: ReindexStep.aliasCreated, + reindexTaskPercComplete: 1, + }, + warnings: [], + hasRequiredPrivileges: true, + }); + + await act(async () => { + testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + }); + + testBed.component.update(); + const { actions, find, exists } = testBed; + + await actions.table.clickDeprecationRowAt('reindex', 0); + + expect(find('reindexChecklistTitle').text()).toEqual('Reindexing process'); + expect(exists('cancelReindexingDocumentsButton')).toBe(false); + }); + }); }); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap index 26119c2b62040..be84bbf143ab3 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap @@ -30,17 +30,6 @@ exports[`ChecklistFlyout renders 1`] = `

- -

- -

-
- -

- -

-
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx index c58230b3563f3..ad1c7d8e94105 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.test.tsx @@ -29,45 +29,69 @@ describe('ReindexProgress', () => { ); expect(wrapper).toMatchInlineSnapshot(` -, - }, - Object { - "status": "incomplete", - "title": , - }, - Object { - "status": "incomplete", - "title": , - }, - Object { - "status": "incomplete", - "title": , - }, - ] - } -/> -`); + + +

+ +

+
+ , + }, + Object { + "status": "incomplete", + "title": , + }, + Object { + "status": "incomplete", + "title": + + + + , + }, + Object { + "status": "incomplete", + "title": , + }, + ] + } + /> +
+ `); }); it('displays errors in the step that failed', () => { @@ -84,30 +108,9 @@ describe('ReindexProgress', () => { cancelReindex={jest.fn()} /> ); - - const aliasStep = wrapper.props().steps[3]; + const aliasStep = (wrapper.find('StepProgress').props() as any).steps[3]; expect(aliasStep.children.props.errorMessage).toEqual( `This is an error that happened on alias switch` ); }); - - it('shows reindexing document progress bar', () => { - const wrapper = shallow( - - ); - - const reindexStep = wrapper.props().steps[2]; - expect(reindexStep.children.type.name).toEqual('ReindexProgressBar'); - expect(reindexStep.children.props.reindexState.reindexTaskPercComplete).toEqual(0.25); - }); }); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx index 7a2c9943691a3..afeac303284f1 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/progress.tsx @@ -12,8 +12,8 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, - EuiProgress, EuiText, + EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -21,6 +21,7 @@ import { ReindexStatus, ReindexStep } from '../../../../../../../common/types'; import { LoadingState } from '../../../../types'; import type { ReindexState } from '../use_reindex_state'; import { StepProgress, StepProgressStep } from './step_progress'; +import { getReindexProgressLabel } from '../../../../../lib/utils'; const ErrorCallout: React.FunctionComponent<{ errorMessage: string | null }> = ({ errorMessage, @@ -39,19 +40,10 @@ const PausedCallout = () => ( /> ); -const ReindexProgressBar: React.FunctionComponent<{ +const CancelReindexingDocumentsButton: React.FunctionComponent<{ reindexState: ReindexState; cancelReindex: () => void; -}> = ({ - reindexState: { lastCompletedStep, status, reindexTaskPercComplete, cancelLoadingState }, - cancelReindex, -}) => { - const progressBar = reindexTaskPercComplete ? ( - - ) : ( - - ); - +}> = ({ reindexState: { lastCompletedStep, status, cancelLoadingState }, cancelReindex }) => { let cancelText: React.ReactNode; switch (cancelLoadingState) { case LoadingState.Loading: @@ -71,7 +63,6 @@ const ReindexProgressBar: React.FunctionComponent<{ ); break; case LoadingState.Error: - cancelText = 'Could not cancel'; cancelText = ( - {progressBar} - - - {cancelText} - - - + + {cancelText} + ); }; @@ -118,7 +105,12 @@ export const ReindexProgress: React.FunctionComponent<{ reindexState: ReindexState; cancelReindex: () => void; }> = (props) => { - const { errorMessage, lastCompletedStep = -1, status } = props.reindexState; + const { + errorMessage, + lastCompletedStep = -1, + status, + reindexTaskPercComplete, + } = props.reindexState; const stepDetails = (thisStep: ReindexStep): Pick => { const previousStep = orderedSteps[orderedSteps.indexOf(thisStep) - 1]; @@ -151,14 +143,23 @@ export const ReindexProgress: React.FunctionComponent<{ } }; - // The reindexing step is special because it combines the starting and complete statuses into a single UI - // with a progress bar. + // The reindexing step is special because it generally lasts longer and can be cancelled mid-flight const reindexingDocsStep = { title: ( - + + + + + {(lastCompletedStep === ReindexStep.newIndexCreated || + lastCompletedStep === ReindexStep.reindexStarted) && ( + + + + )} + ), } as StepProgressStep; @@ -189,7 +190,6 @@ export const ReindexProgress: React.FunctionComponent<{ lastCompletedStep === ReindexStep.reindexStarted ) { reindexingDocsStep.status = 'inProgress'; - reindexingDocsStep.children = ; } else { reindexingDocsStep.status = 'complete'; } @@ -225,5 +225,27 @@ export const ReindexProgress: React.FunctionComponent<{ }, ]; - return ; + return ( + <> + +

+ {status === ReindexStatus.inProgress ? ( + + ) : ( + + )} +

+
+ + + ); }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx index ff2f01befb180..84c6c16d4032a 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/resolution_table_cell.tsx @@ -17,6 +17,7 @@ import { EuiToolTip, } from '@elastic/eui'; import { ReindexStatus } from '../../../../../../common/types'; +import { getReindexProgressLabel } from '../../../../lib/utils'; import { LoadingState } from '../../../types'; import { useReindexContext } from './context'; @@ -99,7 +100,13 @@ export const ReindexResolutionCell: React.FunctionComponent = () => { - {i18nTexts.reindexInProgressText} + + {i18nTexts.reindexInProgressText}{' '} + {getReindexProgressLabel( + reindexState.reindexTaskPercComplete, + reindexState.lastCompletedStep + )} + ); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx index 32a6c75ff6eec..cbeebd00f2bb8 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx @@ -127,7 +127,7 @@ export const useReindexStatus = ({ indexName, api }: { indexName: string; api: A api.sendReindexTelemetryData({ start: true }); - const { data, error } = await api.startReindexTask(indexName); + const { data: reindexOp, error } = await api.startReindexTask(indexName); if (error) { setReindexState({ @@ -139,7 +139,7 @@ export const useReindexStatus = ({ indexName, api }: { indexName: string; api: A return; } - setReindexState(getReindexState(reindexState, data)); + setReindexState(getReindexState(reindexState, { reindexOp })); updateStatus(); }, [api, indexName, reindexState, updateStatus]); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts index 83fc9cabbbecc..37392c832ecf5 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.test.ts @@ -6,7 +6,8 @@ */ import { DEPRECATION_WARNING_UPPER_LIMIT } from '../../../common/constants'; -import { validateRegExpString, getDeprecationsUpperLimit } from './utils'; +import { getDeprecationsUpperLimit, getReindexProgressLabel, validateRegExpString } from './utils'; +import { ReindexStep } from '../../../common/types'; describe('validRegExpString', () => { it('correctly returns false for invalid strings', () => { @@ -35,3 +36,33 @@ describe('getDeprecationsUpperLimit', () => { ); }); }); + +describe('getReindexProgressLabel', () => { + it('returns 0% when the reindex task has just been created', () => { + expect(getReindexProgressLabel(null, ReindexStep.created)).toBe('0%'); + }); + + it('returns 5% when the index has been made read-only', () => { + expect(getReindexProgressLabel(null, ReindexStep.readonly)).toBe('5%'); + }); + + it('returns 10% when the reindexing documents has started, but the progress is null', () => { + expect(getReindexProgressLabel(null, ReindexStep.reindexStarted)).toBe('10%'); + }); + + it('returns 10% when the reindexing documents has started, but the progress is 0', () => { + expect(getReindexProgressLabel(0, ReindexStep.reindexStarted)).toBe('10%'); + }); + + it('returns 53% when the reindexing documents progress is 0.5', () => { + expect(getReindexProgressLabel(0.5, ReindexStep.reindexStarted)).toBe('53%'); + }); + + it('returns 95% when the reindexing documents progress is 1', () => { + expect(getReindexProgressLabel(1, ReindexStep.reindexStarted)).toBe('95%'); + }); + + it('returns 100% when alias has been switched', () => { + expect(getReindexProgressLabel(null, ReindexStep.aliasCreated)).toBe('100%'); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts index b90038e1166ab..f7849d8ec6ece 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/utils.ts @@ -9,6 +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 } from '../../../common/types'; export const validateRegExpString = (s: string) => pipe( @@ -34,3 +35,52 @@ export const getDeprecationsUpperLimit = (count: number) => { return count.toString(); }; + +/* + * Reindexing task consists of 4 steps: making the index read-only, creating a new index, + * reindexing documents into the new index and switching alias from the old to the new index. + * Steps 1, 2 and 4 each contribute 5% to the overall progress. + * Step 3 (reindexing documents) can take a long time for large indices and its progress is calculated + * between 10% and 95% of the overall progress depending on its completeness percentage. + */ +export const getReindexProgressLabel = ( + reindexingDocumentsProgress: number | null, + lastCompletedStep: ReindexStep | undefined +): string => { + let percentsComplete = 0; + switch (lastCompletedStep) { + case ReindexStep.created: + // the reindex task has just started, 0% progress + percentsComplete = 0; + break; + case ReindexStep.readonly: { + // step 1 completed, 5% progress + percentsComplete = 5; + break; + } + case ReindexStep.newIndexCreated: { + // step 2 completed, 10% progress + percentsComplete = 10; + break; + } + case ReindexStep.reindexStarted: { + // step 3 started, 10-95% progress depending on progress of reindexing documents in ES + percentsComplete = + reindexingDocumentsProgress !== null + ? 10 + Math.round(reindexingDocumentsProgress * 85) + : 10; + break; + } + case ReindexStep.reindexCompleted: { + // step 3 completed, only step 4 remaining, 95% progress + percentsComplete = 95; + break; + } + case ReindexStep.aliasCreated: { + // step 4 completed, 100% progress + percentsComplete = 100; + break; + } + } + return `${percentsComplete}%`; +};