-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Add rolling upgrade interstitials to UA #112907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
7b58214
c54644b
ea3bb80
6bf989e
d359b6a
c6a9326
db265e1
240e3b8
7733842
ebafe06
dbc7a8e
983388e
e12329f
3902332
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /* | ||
| * 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 { act } from 'react-dom/test-utils'; | ||
| import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; | ||
|
|
||
| import { AppWithRouter } from '../../../public/application/app'; | ||
| import { WithAppDependencies } from '../helpers'; | ||
|
|
||
| const testBedConfig: TestBedConfig = { | ||
| memoryRouter: { | ||
| initialEntries: [`/overview`], | ||
| componentRoutePath: '/overview', | ||
| }, | ||
| doMountAsync: true, | ||
| }; | ||
|
|
||
| export type AppTestBed = TestBed & { | ||
| actions: ReturnType<typeof createActions>; | ||
| }; | ||
|
|
||
| const createActions = (testBed: TestBed) => { | ||
| const clickDeprecationToggle = async () => { | ||
| const { find, component } = testBed; | ||
|
|
||
| await act(async () => { | ||
| find('deprecationLoggingToggle').simulate('click'); | ||
| }); | ||
|
|
||
| component.update(); | ||
| }; | ||
|
|
||
| return { | ||
| clickDeprecationToggle, | ||
| }; | ||
| }; | ||
|
|
||
| export const setupAppPage = async (overrides?: Record<string, unknown>): Promise<AppTestBed> => { | ||
| const initTestBed = registerTestBed(WithAppDependencies(AppWithRouter, overrides), testBedConfig); | ||
| const testBed = await initTestBed(); | ||
|
|
||
| return { | ||
| ...testBed, | ||
| actions: createActions(testBed), | ||
| }; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| /* | ||
| * 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 { act } from 'react-dom/test-utils'; | ||
|
|
||
| import { setupEnvironment } from '../helpers'; | ||
| import { AppTestBed, setupAppPage } from './app.helpers'; | ||
|
|
||
| describe('Cluster upgrade', () => { | ||
| let testBed: AppTestBed; | ||
| let server: ReturnType<typeof setupEnvironment>['server']; | ||
| let httpRequestsMockHelpers: ReturnType<typeof setupEnvironment>['httpRequestsMockHelpers']; | ||
|
|
||
| beforeEach(() => { | ||
| ({ server, httpRequestsMockHelpers } = setupEnvironment()); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| server.restore(); | ||
| }); | ||
|
|
||
| describe('when user is still preparing for upgrade', () => { | ||
| beforeEach(async () => { | ||
| testBed = await setupAppPage(); | ||
| }); | ||
|
|
||
| test('renders overview', () => { | ||
| const { exists } = testBed; | ||
| expect(exists('overview')).toBe(true); | ||
| expect(exists('isUpgradingMessage')).toBe(false); | ||
| expect(exists('isUpgradeCompleteMessage')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('when cluster is in the process of a rolling upgrade', () => { | ||
| beforeEach(async () => { | ||
| httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, { | ||
| statusCode: 426, | ||
| message: '', | ||
| attributes: { | ||
| allNodesUpgraded: false, | ||
| }, | ||
| }); | ||
|
|
||
| await act(async () => { | ||
| testBed = await setupAppPage(); | ||
| }); | ||
| }); | ||
|
|
||
| test('renders rolling upgrade message', async () => { | ||
| const { component, exists } = testBed; | ||
| component.update(); | ||
| expect(exists('overview')).toBe(false); | ||
| expect(exists('isUpgradingMessage')).toBe(true); | ||
| expect(exists('isUpgradeCompleteMessage')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('when cluster has been upgraded', () => { | ||
| beforeEach(async () => { | ||
| httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, { | ||
| statusCode: 426, | ||
| message: '', | ||
| attributes: { | ||
| allNodesUpgraded: true, | ||
| }, | ||
| }); | ||
|
|
||
| await act(async () => { | ||
| testBed = await setupAppPage(); | ||
| }); | ||
| }); | ||
|
|
||
| test('renders upgrade complete message', () => { | ||
| const { component, exists } = testBed; | ||
| component.update(); | ||
| expect(exists('overview')).toBe(false); | ||
| expect(exists('isUpgradingMessage')).toBe(false); | ||
| expect(exists('isUpgradeCompleteMessage')).toBe(true); | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,27 +5,109 @@ | |
| * 2.0. | ||
| */ | ||
|
|
||
| import React from 'react'; | ||
| import React, { useState, useEffect } from 'react'; | ||
| import { Router, Switch, Route, Redirect } from 'react-router-dom'; | ||
| import { FormattedMessage } from '@kbn/i18n/react'; | ||
| import { EuiEmptyPrompt, EuiPageContent } from '@elastic/eui'; | ||
| import { ScopedHistory } from 'src/core/public'; | ||
|
|
||
| import { RedirectAppLinks } from '../../../../../src/plugins/kibana_react/public'; | ||
| import { API_BASE_PATH } from '../../common/constants'; | ||
| import { ClusterUpgradeState } from '../../common/types'; | ||
| import { APP_WRAPPER_CLASS, GlobalFlyout, AuthorizationProvider } from '../shared_imports'; | ||
| import { AppDependencies } from '../types'; | ||
| import { API_BASE_PATH } from '../../common/constants'; | ||
| import { AppContextProvider, useAppContext } from './app_context'; | ||
| import { EsDeprecations, ComingSoonPrompt, KibanaDeprecations, Overview } from './components'; | ||
|
|
||
| const { GlobalFlyoutProvider } = GlobalFlyout; | ||
|
|
||
| const App: React.FunctionComponent = () => { | ||
| const { isReadOnlyMode } = useAppContext(); | ||
| const AppWithoutPolling: React.FunctionComponent = () => { | ||
| const { | ||
| isReadOnlyMode, | ||
| services: { api }, | ||
| } = useAppContext(); | ||
|
|
||
| const [clusterUpgradeState, setClusterUpradeState] = | ||
| useState<ClusterUpgradeState>('isPreparingForUpgrade'); | ||
|
|
||
| useEffect(() => { | ||
| api.onClusterUpgradeStateChange((newClusterUpgradeState: ClusterUpgradeState) => { | ||
| setClusterUpradeState(newClusterUpgradeState); | ||
| }); | ||
| }, [api]); | ||
|
|
||
| // Read-only mode will be enabled up until the last minor before the next major release | ||
| if (isReadOnlyMode) { | ||
| return <ComingSoonPrompt />; | ||
| } | ||
|
|
||
| if (clusterUpgradeState === 'isUpgrading') { | ||
| return ( | ||
| <EuiPageContent | ||
| hasShadow={false} | ||
| paddingSize="none" | ||
| verticalPosition="center" | ||
| horizontalPosition="center" | ||
| data-test-subj="isUpgradingMessage" | ||
| > | ||
| <EuiEmptyPrompt | ||
| iconType="logoElasticsearch" | ||
| title={ | ||
| <h1> | ||
| <FormattedMessage | ||
| id="xpack.upgradeAssistant.upgradingTitle" | ||
| defaultMessage="Your cluster is upgrading" | ||
| /> | ||
| </h1> | ||
| } | ||
| body={ | ||
| <p> | ||
| <FormattedMessage | ||
| id="xpack.upgradeAssistant.upgradingDescription" | ||
| defaultMessage="One or more Elasticsearch nodes have a newer version of | ||
| Elasticsearch than Kibana. Once all your nodes are upgraded, upgrade Kibana." | ||
| /> | ||
| </p> | ||
| } | ||
| data-test-subj="emptyPrompt" | ||
| /> | ||
| </EuiPageContent> | ||
| ); | ||
| } | ||
|
|
||
| if (clusterUpgradeState === 'isUpgradeComplete') { | ||
| return ( | ||
| <EuiPageContent | ||
| hasShadow={false} | ||
| paddingSize="none" | ||
| verticalPosition="center" | ||
| horizontalPosition="center" | ||
| data-test-subj="isUpgradeCompleteMessage" | ||
| > | ||
| <EuiEmptyPrompt | ||
| iconType="logoElasticsearch" | ||
| title={ | ||
| <h1> | ||
| <FormattedMessage | ||
| id="xpack.upgradeAssistant.upgradedTitle" | ||
| defaultMessage="Your cluster has been upgraded" | ||
| /> | ||
| </h1> | ||
| } | ||
| body={ | ||
| <p> | ||
| <FormattedMessage | ||
| id="xpack.upgradeAssistant.upgradedDescription" | ||
| defaultMessage="All Elasticsearch nodes have been upgraded. You may now upgrade Kibana." | ||
| /> | ||
| </p> | ||
| } | ||
| data-test-subj="emptyPrompt" | ||
| /> | ||
| </EuiPageContent> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <Switch> | ||
| <Route exact path="/overview" component={Overview} /> | ||
|
|
@@ -36,6 +118,18 @@ const App: React.FunctionComponent = () => { | |
| ); | ||
| }; | ||
|
|
||
| export const App: React.FunctionComponent = () => { | ||
| const { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you think about merging this component and AppWithRouter? they both feel quite small and it might reduce some of the overhead of reading this file which already has a bunch of components |
||
| services: { api }, | ||
| } = useAppContext(); | ||
|
|
||
| // This is a hack to avoid the app getting stuck in an infinite render loop, | ||
| // as noted in api.ts. | ||
| api.useLoadClusterUpgradeStatus(); | ||
|
||
|
|
||
| return <AppWithoutPolling />; | ||
| }; | ||
|
|
||
| export const AppWithRouter = ({ history }: { history: ScopedHistory }) => { | ||
| return ( | ||
| <Router history={history}> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,7 +20,7 @@ import { | |
| } from '@elastic/eui'; | ||
| import { i18n } from '@kbn/i18n'; | ||
|
|
||
| import { ResponseError } from '../../../../lib/api'; | ||
| import { ResponseError } from '../../../../../../common/types'; | ||
| import { DeprecationLoggingPreviewProps } from '../../../types'; | ||
|
|
||
| import './_deprecation_logging_toggle.scss'; | ||
|
|
@@ -79,7 +79,18 @@ const ErrorDetailsLink = ({ error }: { error: ResponseError }) => { | |
| ); | ||
| }; | ||
|
|
||
| export const DeprecationLoggingToggle: FunctionComponent<DeprecationLoggingPreviewProps> = ({ | ||
| type Props = Pick< | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL ! |
||
| DeprecationLoggingPreviewProps, | ||
| | 'isDeprecationLogIndexingEnabled' | ||
| | 'isLoading' | ||
| | 'isUpdating' | ||
| | 'fetchError' | ||
| | 'updateError' | ||
| | 'resendRequest' | ||
| | 'toggleLogging' | ||
| >; | ||
|
|
||
| export const DeprecationLoggingToggle: FunctionComponent<Props> = ({ | ||
| isDeprecationLogIndexingEnabled, | ||
| isLoading, | ||
| isUpdating, | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add types for
attributesalso? It seems it only holdsallNodesUpgraded: boolean