diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx new file mode 100644 index 0000000000000..23726e05b895d --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx @@ -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 { App } from '../../../public/application/app'; +import { WithAppDependencies } from '../helpers'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + initialEntries: [`/overview`], + componentRoutePath: '/overview', + }, + doMountAsync: true, +}; + +export type AppTestBed = TestBed & { + actions: ReturnType; +}; + +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): Promise => { + const initTestBed = registerTestBed(WithAppDependencies(App, overrides), testBedConfig); + const testBed = await initTestBed(); + + return { + ...testBed, + actions: createActions(testBed), + }; +}; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx new file mode 100644 index 0000000000000..043c649b39bc2 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx @@ -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['server']; + let httpRequestsMockHelpers: ReturnType['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); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/app_context.mock.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/app_context.mock.ts index 46e3deda36fc9..8f142dcdb4b6d 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/app_context.mock.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/app_context.mock.ts @@ -80,4 +80,7 @@ export const getAppContextMock = () => ({ isCloudEnabled: false, }, }, + clusterUpgradeState: 'isPreparingForUpgrade', + isClusterUpgradeStateError: () => {}, + handleClusterUpgradeStateError: () => {}, }); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts index b4ae504fb3b1a..7903ca58ac18a 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts @@ -6,13 +6,14 @@ */ import sinon, { SinonFakeServer } from 'sinon'; + import { API_BASE_PATH } from '../../../common/constants'; import { CloudBackupStatus, ESUpgradeStatus, DeprecationLoggingStatus, + ResponseError, } from '../../../common/types'; -import { ResponseError } from '../../../public/application/lib/api'; // Register helpers to mock HTTP Requests const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { diff --git a/x-pack/plugins/upgrade_assistant/common/constants.ts b/x-pack/plugins/upgrade_assistant/common/constants.ts index db8e2a707baa8..0b0a5b57397e0 100644 --- a/x-pack/plugins/upgrade_assistant/common/constants.ts +++ b/x-pack/plugins/upgrade_assistant/common/constants.ts @@ -34,6 +34,7 @@ export const DEPRECATION_LOGS_SOURCE_ID = 'deprecation_logs'; export const DEPRECATION_LOGS_INDEX = '.logs-deprecation.elasticsearch-default'; export const DEPRECATION_LOGS_INDEX_PATTERN = '.logs-deprecation.elasticsearch-default'; +export const CLUSTER_UPGRADE_STATUS_POLL_INTERVAL_MS = 45000; export const CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS = 60000; export const DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS = 15000; export const SYSTEM_INDICES_MIGRATION_POLL_INTERVAL_MS = 15000; diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index 3a7583bc50219..9752fa326082a 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -13,6 +13,16 @@ import { SavedObject, SavedObjectAttributes } from 'src/core/public'; export type DeprecationSource = 'Kibana' | 'Elasticsearch'; +export type ClusterUpgradeState = 'isPreparingForUpgrade' | 'isUpgrading' | 'isUpgradeComplete'; + +export interface ResponseError { + statusCode: number; + message: string | Error; + attributes?: { + allNodesUpgraded: boolean; + }; +} + export enum ReindexStep { // Enum values are spaced out by 10 to give us room to insert steps in between. created = 0, diff --git a/x-pack/plugins/upgrade_assistant/public/application/app.tsx b/x-pack/plugins/upgrade_assistant/public/application/app.tsx index be2092b46a79a..9ac90e5d81f48 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/app.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/app.tsx @@ -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, EuiLoadingSpinner } 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 AppHandlingClusterUpgradeState: React.FunctionComponent = () => { + const { + isReadOnlyMode, + services: { api }, + } = useAppContext(); + + const [clusterUpgradeState, setClusterUpradeState] = + useState('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 ; } + if (clusterUpgradeState === 'isUpgrading') { + return ( + + + + + } + body={ +

+ +

+ } + data-test-subj="emptyPrompt" + /> +
+ ); + } + + if (clusterUpgradeState === 'isUpgradeComplete') { + return ( + + + + + } + body={ +

+ +

+ } + data-test-subj="emptyPrompt" + /> +
+ ); + } + return ( @@ -36,10 +118,37 @@ const App: React.FunctionComponent = () => { ); }; -export const AppWithRouter = ({ history }: { history: ScopedHistory }) => { +export const App = ({ history }: { history: ScopedHistory }) => { + const { + services: { api }, + } = useAppContext(); + + // Poll the API to detect when the cluster is either in the middle of + // a rolling upgrade or has completed one. We need to create two separate + // components: one to call this hook and one to handle state changes. + // This is because the implementation of this hook calls the state-change + // callbacks on every render, which will get the UI stuck in an infinite + // render loop if the same component both called the hook and handled + // the state changes it triggers. + const { isLoading, isInitialRequest } = api.useLoadClusterUpgradeStatus(); + + // Prevent flicker of the underlying UI while we wait for the status to fetch. + if (isLoading && isInitialRequest) { + return ( + + } /> + + ); + } + return ( - + ); }; @@ -56,7 +165,7 @@ export const RootComponent = (dependencies: AppDependencies) => { - + diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx index 24c1897fbdd02..3c19268a293f0 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx @@ -23,8 +23,12 @@ import { EuiSpacer, EuiCallOut, } from '@elastic/eui'; -import { EnrichedDeprecationInfo, IndexSettingAction } from '../../../../../../common/types'; -import type { ResponseError } from '../../../../lib/api'; + +import { + EnrichedDeprecationInfo, + IndexSettingAction, + ResponseError, +} from '../../../../../../common/types'; import type { Status } from '../../../types'; import { DeprecationBadge } from '../../../shared'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx index 997cf476c8378..28fb11334fb3d 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx @@ -7,10 +7,9 @@ import React, { useState, useEffect, useCallback } from 'react'; import { EuiTableRowCell } from '@elastic/eui'; -import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { EnrichedDeprecationInfo, ResponseError } from '../../../../../../common/types'; import { GlobalFlyout } from '../../../../../shared_imports'; import { useAppContext } from '../../../../app_context'; -import type { ResponseError } from '../../../../lib/api'; import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; import { DeprecationTableColumns, Status } from '../../../types'; import { IndexSettingsResolutionCell } from './resolution_table_cell'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx index 39f7c3d37ad52..6725ba098e3c9 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/use_snapshot_state.tsx @@ -7,7 +7,8 @@ import { useRef, useCallback, useState, useEffect } from 'react'; -import { ApiService, ResponseError } from '../../../../lib/api'; +import { ResponseError } from '../../../../../../common/types'; +import { ApiService } from '../../../../lib/api'; import { Status } from '../../../types'; const POLL_INTERVAL_MS = 1000; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx index 6de099fe05ef5..0df75a426901e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx @@ -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 = ({ +type Props = Pick< + DeprecationLoggingPreviewProps, + | 'isDeprecationLogIndexingEnabled' + | 'isLoading' + | 'isUpdating' + | 'fetchError' + | 'updateError' + | 'resendRequest' + | 'toggleLogging' +>; + +export const DeprecationLoggingToggle: FunctionComponent = ({ isDeprecationLogIndexingEnabled, isLoading, isUpdating, diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx index ee0935e102f9c..42ab75a3ee3f4 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx @@ -106,12 +106,23 @@ const FixLogsStep: FunctionComponent = ({ hasPrivileges, privilegesMissing, }) => { - const state = useDeprecationLogging(); const { services: { core: { docLinks }, }, } = useAppContext(); + + const { + isDeprecationLogIndexingEnabled, + onlyDeprecationLogWritingEnabled, + isLoading, + isUpdating, + fetchError, + updateError, + resendRequest, + toggleLogging, + } = useDeprecationLogging(); + const [checkpoint, setCheckpoint] = useState(loadLogsCheckpoint()); useEffect(() => { @@ -119,13 +130,13 @@ const FixLogsStep: FunctionComponent = ({ }, [checkpoint]); useEffect(() => { - if (!state.isDeprecationLogIndexingEnabled) { + if (!isDeprecationLogIndexingEnabled) { setIsComplete(false); } // Depending upon setIsComplete would create an infinite loop. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.isDeprecationLogIndexingEnabled]); + }, [isDeprecationLogIndexingEnabled]); return ( <> @@ -134,10 +145,18 @@ const FixLogsStep: FunctionComponent = ({ - + - {state.onlyDeprecationLogWritingEnabled && ( + {onlyDeprecationLogWritingEnabled && ( <> = ({ )} - {!hasPrivileges && state.isDeprecationLogIndexingEnabled && ( + {!hasPrivileges && isDeprecationLogIndexingEnabled && ( <> = ({ )} - {hasPrivileges && state.isDeprecationLogIndexingEnabled && ( + {hasPrivileges && isDeprecationLogIndexingEnabled && ( <> diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/use_deprecation_logging.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/use_deprecation_logging.ts index 45ea4e09accfe..e25fd91ae2c52 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/use_deprecation_logging.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/use_deprecation_logging.ts @@ -9,8 +9,8 @@ import { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; +import { ResponseError } from '../../../../../common/types'; import { useAppContext } from '../../../app_context'; -import { ResponseError } from '../../../lib/api'; import { DeprecationLoggingPreviewProps } from '../../types'; const i18nTexts = { diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/migrate_system_indices/use_migrate_system_indices.ts b/x-pack/plugins/upgrade_assistant/public/application/components/overview/migrate_system_indices/use_migrate_system_indices.ts index d6e9bd2a9d5ff..0d4ea9bfaf35c 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/migrate_system_indices/use_migrate_system_indices.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/migrate_system_indices/use_migrate_system_indices.ts @@ -8,11 +8,11 @@ import { useCallback, useState, useEffect } from 'react'; import useInterval from 'react-use/lib/useInterval'; -import { SystemIndicesFlyout, SystemIndicesFlyoutProps } from './flyout'; -import { useAppContext } from '../../../app_context'; -import type { ResponseError } from '../../../lib/api'; -import { GlobalFlyout } from '../../../../shared_imports'; import { SYSTEM_INDICES_MIGRATION_POLL_INTERVAL_MS } from '../../../../../common/constants'; +import type { ResponseError } from '../../../../../common/types'; +import { GlobalFlyout } from '../../../../shared_imports'; +import { useAppContext } from '../../../app_context'; +import { SystemIndicesFlyout, SystemIndicesFlyoutProps } from './flyout'; const FLYOUT_ID = 'migrateSystemIndicesFlyout'; const { useGlobalFlyout } = GlobalFlyout; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/types.ts b/x-pack/plugins/upgrade_assistant/public/application/components/types.ts index 6b52f7cece514..31dd9cf43656d 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/types.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ResponseError } from '../lib/api'; +import { ResponseError } from '../../../common/types'; export enum LoadingState { Loading, diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts index 24a45fa1018fe..3342435a6d46e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts @@ -10,10 +10,13 @@ import { HttpSetup } from 'src/core/public'; import { ESUpgradeStatus, CloudBackupStatus, + ClusterUpgradeState, + ResponseError, SystemIndicesMigrationStatus, } from '../../../common/types'; import { API_BASE_PATH, + CLUSTER_UPGRADE_STATUS_POLL_INTERVAL_MS, DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS, CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS, } from '../../../common/constants'; @@ -25,35 +28,72 @@ import { useRequest as _useRequest, } from '../../shared_imports'; -export interface ResponseError { - statusCode: number; - message: string | Error; - attributes?: Record; -} +type ClusterUpgradeStateListener = (clusterUpgradeState: ClusterUpgradeState) => void; export class ApiService { private client: HttpSetup | undefined; - - private useRequest(config: UseRequestConfig) { - if (!this.client) { - throw new Error('API service has not be initialized.'); + private clusterUpgradeStateListeners: ClusterUpgradeStateListener[] = []; + + private handleClusterUpgradeError(error: ResponseError | null) { + const isClusterUpgradeError = Boolean(error && error.statusCode === 426); + if (isClusterUpgradeError) { + const clusterUpgradeState = error!.attributes!.allNodesUpgraded + ? 'isUpgradeComplete' + : 'isUpgrading'; + this.clusterUpgradeStateListeners.forEach((listener) => listener(clusterUpgradeState)); } - return _useRequest(this.client, config); } - private sendRequest( + private useRequest(config: UseRequestConfig) { + if (!this.client) { + throw new Error('API service has not been initialized.'); + } + const response = _useRequest(this.client, config); + // NOTE: This will cause an infinite render loop in any component that both + // consumes the hook calling this useRequest function and also handles + // cluster upgrade errors. Note that sendRequest doesn't have this problem. + // + // This is due to React's fundamental expectation that hooks be idempotent, + // so it can render a component as many times as necessary and thereby call + // the hook on each render without worrying about that triggering subsequent + // renders. + // + // In this case we call handleClusterUpgradeError every time useRequest is + // called, which is on every render. If handling the cluster upgrade error + // causes a state change in the consuming component, that will trigger a + // render, which will call useRequest again, calling handleClusterUpgradeError, + // causing a state change in the consuming component, and so on. + this.handleClusterUpgradeError(response.error); + return response; + } + + private async sendRequest( config: SendRequestConfig - ): Promise> { + ): Promise> { if (!this.client) { - throw new Error('API service has not be initialized.'); + throw new Error('API service has not been initialized.'); } - return _sendRequest(this.client, config); + const response = await _sendRequest(this.client, config); + this.handleClusterUpgradeError(response.error); + return response; } public setup(httpClient: HttpSetup): void { this.client = httpClient; } + public onClusterUpgradeStateChange(listener: ClusterUpgradeStateListener) { + this.clusterUpgradeStateListeners.push(listener); + } + + public useLoadClusterUpgradeStatus() { + return this.useRequest({ + path: `${API_BASE_PATH}/cluster_upgrade_status`, + method: 'get', + pollIntervalMs: CLUSTER_UPGRADE_STATUS_POLL_INTERVAL_MS, + }); + } + public useLoadCloudBackupStatus() { return this.useRequest({ path: `${API_BASE_PATH}/cloud_backup_status`, @@ -86,13 +126,11 @@ export class ApiService { } public async sendPageTelemetryData(telemetryData: { [tabName: string]: boolean }) { - const result = await this.sendRequest({ + return await this.sendRequest({ path: `${API_BASE_PATH}/stats/ui_open`, method: 'put', body: JSON.stringify(telemetryData), }); - - return result; } public useLoadDeprecationLogging() { @@ -106,13 +144,11 @@ export class ApiService { } public async updateDeprecationLogging(loggingData: { isEnabled: boolean }) { - const result = await this.sendRequest({ + return await this.sendRequest({ path: `${API_BASE_PATH}/deprecation_logging`, method: 'put', body: JSON.stringify(loggingData), }); - - return result; } public getDeprecationLogsCount(from: string) { @@ -134,34 +170,28 @@ export class ApiService { } public async updateIndexSettings(indexName: string, settings: string[]) { - const result = await this.sendRequest({ + return await this.sendRequest({ path: `${API_BASE_PATH}/${indexName}/index_settings`, method: 'post', body: { settings: JSON.stringify(settings), }, }); - - return result; } public async upgradeMlSnapshot(body: { jobId: string; snapshotId: string }) { - const result = await this.sendRequest({ + return await this.sendRequest({ path: `${API_BASE_PATH}/ml_snapshots`, method: 'post', body, }); - - return result; } public async deleteMlSnapshot({ jobId, snapshotId }: { jobId: string; snapshotId: string }) { - const result = await this.sendRequest({ + return await this.sendRequest({ path: `${API_BASE_PATH}/ml_snapshots/${jobId}/${snapshotId}`, method: 'delete', }); - - return result; } public async getMlSnapshotUpgradeStatus({ @@ -177,14 +207,21 @@ export class ApiService { }); } + public useLoadMlUpgradeMode() { + return this.useRequest<{ + mlUpgradeModeEnabled: boolean; + }>({ + path: `${API_BASE_PATH}/ml_upgrade_mode`, + method: 'get', + }); + } + public async sendReindexTelemetryData(telemetryData: { [key: string]: boolean }) { - const result = await this.sendRequest({ + return await this.sendRequest({ path: `${API_BASE_PATH}/stats/ui_reindex`, method: 'put', body: JSON.stringify(telemetryData), }); - - return result; } public async getReindexStatus(indexName: string) { @@ -207,15 +244,6 @@ export class ApiService { method: 'post', }); } - - public useLoadMlUpgradeMode() { - return this.useRequest<{ - mlUpgradeModeEnabled: boolean; - }>({ - path: `${API_BASE_PATH}/ml_upgrade_mode`, - method: 'get', - }); - } } export const apiService = new ApiService(); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts index 889568fb7a412..9581ce872a288 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/get_es_deprecation_error.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ResponseError } from './api'; +import { ResponseError } from '../../../common/types'; const i18nTexts = { permissionsError: i18n.translate( diff --git a/x-pack/plugins/upgrade_assistant/public/shared_imports.ts b/x-pack/plugins/upgrade_assistant/public/shared_imports.ts index 1d0b1ae51f30f..8fbf87c105214 100644 --- a/x-pack/plugins/upgrade_assistant/public/shared_imports.ts +++ b/x-pack/plugins/upgrade_assistant/public/shared_imports.ts @@ -11,7 +11,6 @@ export { SendRequestResponse, useRequest, UseRequestConfig, - UseRequestResponse, SectionLoading, GlobalFlyout, WithPrivileges, diff --git a/x-pack/plugins/upgrade_assistant/public/types.ts b/x-pack/plugins/upgrade_assistant/public/types.ts index 4d47c10fdeec2..a0e6c5f7d1dff 100644 --- a/x-pack/plugins/upgrade_assistant/public/types.ts +++ b/x-pack/plugins/upgrade_assistant/public/types.ts @@ -10,6 +10,7 @@ import { ManagementSetup } from 'src/plugins/management/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { SharePluginSetup } from 'src/plugins/share/public'; import { CoreStart } from 'src/core/public'; + import { CloudSetup } from '../../cloud/public'; import { LicensingPluginStart } from '../../licensing/public'; import { BreadcrumbService } from './application/lib/breadcrumbs'; diff --git a/x-pack/plugins/upgrade_assistant/server/routes/cluster_upgrade_status.ts b/x-pack/plugins/upgrade_assistant/server/routes/cluster_upgrade_status.ts new file mode 100644 index 0000000000000..4ae1205d2daef --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/routes/cluster_upgrade_status.ts @@ -0,0 +1,21 @@ +/* + * 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 { API_BASE_PATH } from '../../common/constants'; +import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; +import { RouteDependencies } from '../types'; + +export function registerClusterUpgradeStatusRoutes({ router }: RouteDependencies) { + router.get( + { path: `${API_BASE_PATH}/cluster_upgrade_status`, validate: false }, + // We're just depending on the version check to return a 426. + // Otherwise we just return a 200. + versionCheckHandlerWrapper(async (context, request, response) => { + return response.ok(); + }) + ); +} diff --git a/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts b/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts index ec02e814b2a8d..e6a92af53b143 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts @@ -9,6 +9,7 @@ import { RouteDependencies } from '../types'; import { registerAppRoutes } from './app'; import { registerCloudBackupStatusRoutes } from './cloud_backup_status'; +import { registerClusterUpgradeStatusRoutes } from './cluster_upgrade_status'; import { registerSystemIndicesMigrationRoutes } from './system_indices_migration'; import { registerESDeprecationRoutes } from './es_deprecations'; import { registerDeprecationLoggingRoutes } from './deprecation_logging'; @@ -22,6 +23,7 @@ import { registerUpgradeStatusRoute } from './status'; export function registerRoutes(dependencies: RouteDependencies, getWorker: () => ReindexWorker) { registerAppRoutes(dependencies); registerCloudBackupStatusRoutes(dependencies); + registerClusterUpgradeStatusRoutes(dependencies); registerSystemIndicesMigrationRoutes(dependencies); registerESDeprecationRoutes(dependencies); registerDeprecationLoggingRoutes(dependencies); diff --git a/x-pack/plugins/upgrade_assistant/server/routes/status.ts b/x-pack/plugins/upgrade_assistant/server/routes/status.ts index aef97dcb7bd71..ce9bb2e1c55d0 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/status.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/status.ts @@ -12,6 +12,9 @@ import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; import { getKibanaUpgradeStatus } from '../lib/kibana_status'; import { RouteDependencies } from '../types'; +/** + * Note that this route is primarily intended for consumption by Cloud. + */ export function registerUpgradeStatusRoute({ router, lib: { handleEsError } }: RouteDependencies) { router.get( {