[8.0] [UA] ES deprecation logs in its own page (#118659) (#119134) (#119345)

This commit is contained in:
Sébastien Loix 2021-11-22 20:25:28 +00:00 committed by GitHub
parent a1f556104f
commit e24749a640
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 468 additions and 340 deletions

View file

@ -222,6 +222,7 @@ export class DocLinksService {
mappingTermVector: `${ELASTICSEARCH_DOCS}term-vector.html`,
mappingTypesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`,
migrateIndexAllocationFilters: `${ELASTICSEARCH_DOCS}migrate-index-allocation-filters.html`,
migrationApiDeprecation: `${ELASTICSEARCH_DOCS}migration-api-deprecation.html`,
nodeRoles: `${ELASTICSEARCH_DOCS}modules-node.html#node-roles`,
releaseHighlights: `${ELASTICSEARCH_DOCS}release-highlights.html`,
remoteClusters: `${ELASTICSEARCH_DOCS}remote-clusters.html`,

View file

@ -22,7 +22,7 @@ interface Props {
isLoading: boolean;
hasPrivileges: boolean;
privilegesMissing: MissingPrivileges;
}) => JSX.Element;
}) => JSX.Element | null;
}
type Privilege = [string, string];

View file

@ -17584,7 +17584,7 @@
"xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "フィルターリストを表示するパーミッションがありません",
"xpack.ml.ruleEditor.scopeSection.scopeTitle": "範囲",
"xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "ルールを作成",
"xpack.ml.ruleEditor.selectRuleAction.orText": "OR ",
"xpack.ml.ruleEditor.selectRuleAction.orText": "OR ",
"xpack.ml.ruleEditor.typicalAppliesTypeText": "通常",
"xpack.ml.sampleDataLinkLabel": "ML ジョブ",
"xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "異常検知",
@ -23602,9 +23602,9 @@
"xpack.securitySolution.open.timeline.showingLabel": "表示中:",
"xpack.securitySolution.open.timeline.singleTemplateLabel": "テンプレート",
"xpack.securitySolution.open.timeline.singleTimelineLabel": "タイムライン",
"xpack.securitySolution.open.timeline.successfullyDeletedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}の削除が正常に完了しました",
"xpack.securitySolution.open.timeline.successfullyDeletedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}の削除が正常に完了しました",
"xpack.securitySolution.open.timeline.successfullyDeletedTimelineTemplatesTitle": "{totalTimelineTemplates, plural, =0 {すべてのタイムライン} other {{totalTimelineTemplates}個のタイムラインテンプレート}}が正常に削除されました",
"xpack.securitySolution.open.timeline.successfullyExportedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}のエクスポートが正常に完了しました",
"xpack.securitySolution.open.timeline.successfullyExportedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}のエクスポートが正常に完了しました",
"xpack.securitySolution.open.timeline.successfullyExportedTimelineTemplatesTitle": "{totalTimelineTemplates, plural, =0 {すべてのタイムライン} other {{totalTimelineTemplates} タイムラインテンプレート}}が正常にエクスポートされました",
"xpack.securitySolution.open.timeline.timelineNameTableHeader": "タイムライン名",
"xpack.securitySolution.open.timeline.timelineTemplateNameTableHeader": "テンプレート名",
@ -26696,9 +26696,7 @@
"xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage": "ログ状態を更新できませんでした。",
"xpack.upgradeAssistant.overview.deprecationsCountCheckpointTitle": "廃止予定の問題を解決して変更を検証",
"xpack.upgradeAssistant.overview.documentationLinkText": "ドキュメント",
"xpack.upgradeAssistant.overview.fixIssuesStepDescription": "Elastic 8.xとの互換になるように、ElasticsearchおよびKibanaデプロイを更新します。アップグレード前に、重大な問題を解決する必要があります。警告の問題は独自の裁量で無視できます。",
"xpack.upgradeAssistant.overview.fixIssuesStepTitle": "廃止予定設定を確認し、問題を解決",
"xpack.upgradeAssistant.overview.identifyStepTitle": "廃止予定APIの使用を特定し、アプリケーションを更新",
"xpack.upgradeAssistant.overview.loadingLogsLabel": "廃止予定ログ収集状態を読み込んでいます...",
"xpack.upgradeAssistant.overview.observe.discoveryDescription": "廃止予定ログを検索およびフィルターし、必要な変更のタイプを把握します。",
"xpack.upgradeAssistant.overview.observe.observabilityDescription": "使用中のAPIのうち廃止予定のAPIと、更新が必要なアプリケーションを特定できます。",

View file

@ -17849,7 +17849,7 @@
"xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "您无权查看筛选列表",
"xpack.ml.ruleEditor.scopeSection.scopeTitle": "范围",
"xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "创建规则",
"xpack.ml.ruleEditor.selectRuleAction.orText": "或 ",
"xpack.ml.ruleEditor.selectRuleAction.orText": "或 ",
"xpack.ml.ruleEditor.typicalAppliesTypeText": "典型",
"xpack.ml.sampleDataLinkLabel": "ML 作业",
"xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "异常检测",
@ -27154,9 +27154,7 @@
"xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage": "无法更新日志记录状态。",
"xpack.upgradeAssistant.overview.deprecationsCountCheckpointTitle": "解决弃用问题并验证您的更改",
"xpack.upgradeAssistant.overview.documentationLinkText": "文档",
"xpack.upgradeAssistant.overview.fixIssuesStepDescription": "更新您的 Elasticsearch 和 Kibana 部署以兼容 Elastic 8.x。在升级之前必须解决紧急问题。您可以酌情忽略警告问题。",
"xpack.upgradeAssistant.overview.fixIssuesStepTitle": "复查已弃用设置并解决问题",
"xpack.upgradeAssistant.overview.identifyStepTitle": "识别已弃用 API 的使用并更新您的应用程序",
"xpack.upgradeAssistant.overview.loadingLogsLabel": "正在加载弃用日志收集状态……",
"xpack.upgradeAssistant.overview.observe.discoveryDescription": "搜索并筛选弃用日志以了解需要进行的更改类型。",
"xpack.upgradeAssistant.overview.observe.observabilityDescription": "深入了解正在使用哪些已弃用 API 以及需要更新哪些应用程序。",

View file

@ -36,9 +36,14 @@ describe('Cluster upgrade', () => {
});
});
// The way we detect if we are currently upgrading or if the upgrade has been completed is if
// we ever get back a 426 error in *any* API response that UA makes. For that reason we can
// just mock one of the APIs that are being called from the overview page to return an error
// in order to trigger these interstitial states. In this case we're going to mock the
// `es deprecations` response.
describe('when cluster is in the process of a rolling upgrade', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, {
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, {
statusCode: 426,
message: '',
attributes: {
@ -62,7 +67,7 @@ describe('Cluster upgrade', () => {
describe('when cluster has been upgraded', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, {
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, {
statusCode: 426,
message: '',
attributes: {

View file

@ -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 { act } from 'react-dom/test-utils';
import { registerTestBed, TestBed, AsyncTestBedConfig } from '@kbn/test/jest';
import { EsDeprecationLogs } from '../../../public/application/components/es_deprecation_logs';
import { WithAppDependencies } from '../helpers';
const testBedConfig: AsyncTestBedConfig = {
memoryRouter: {
initialEntries: ['/es_deprecation_logs'],
componentRoutePath: '/es_deprecation_logs',
},
doMountAsync: true,
};
export type EsDeprecationLogsTestBed = TestBed & {
actions: ReturnType<typeof createActions>;
};
const createActions = (testBed: TestBed) => {
/**
* User Actions
*/
const clickDeprecationToggle = async () => {
const { find, component } = testBed;
await act(async () => {
find('deprecationLoggingToggle').simulate('click');
});
component.update();
};
const clickRetryButton = async () => {
const { find, component } = testBed;
await act(async () => {
find('retryButton').simulate('click');
});
component.update();
};
const clickResetButton = async () => {
const { find, component } = testBed;
await act(async () => {
find('resetLastStoredDate').simulate('click');
});
component.update();
};
return {
clickDeprecationToggle,
clickRetryButton,
clickResetButton,
};
};
export const setupESDeprecationLogsPage = async (
overrides?: Record<string, unknown>
): Promise<EsDeprecationLogsTestBed> => {
const initTestBed = registerTestBed(
WithAppDependencies(EsDeprecationLogs, overrides),
testBedConfig
);
const testBed = await initTestBed();
return {
...testBed,
actions: createActions(testBed),
};
};

View file

@ -6,13 +6,24 @@
*/
import { act } from 'react-dom/test-utils';
import {
EsDeprecationLogsTestBed,
setupESDeprecationLogsPage,
} from './es_deprecation_logs.helpers';
import { setupEnvironment, advanceTime } from '../helpers';
import { DeprecationLoggingStatus } from '../../../common/types';
import {
DEPRECATION_LOGS_INDEX,
DEPRECATION_LOGS_SOURCE_ID,
DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS,
} from '../../../common/constants';
// Once the logs team register the kibana locators in their app, we should be able
// to remove this mock and follow a similar approach to how discover link is tested.
// See: https://github.com/elastic/kibana/issues/104855
const MOCKED_TIME = '2021-09-05T10:49:01.805Z';
jest.mock('../../../../public/application/lib/logs_checkpoint', () => {
const originalModule = jest.requireActual('../../../../public/application/lib/logs_checkpoint');
jest.mock('../../../public/application/lib/logs_checkpoint', () => {
const originalModule = jest.requireActual('../../../public/application/lib/logs_checkpoint');
return {
__esModule: true,
@ -21,83 +32,30 @@ jest.mock('../../../../public/application/lib/logs_checkpoint', () => {
};
});
import { DeprecationLoggingStatus } from '../../../../common/types';
import { OverviewTestBed, setupOverviewPage } from '../overview.helpers';
import { setupEnvironment, advanceTime } from '../../helpers';
import {
DEPRECATION_LOGS_INDEX,
DEPRECATION_LOGS_SOURCE_ID,
DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS,
} from '../../../../common/constants';
const getLoggingResponse = (toggle: boolean): DeprecationLoggingStatus => ({
isDeprecationLogIndexingEnabled: toggle,
isDeprecationLoggingEnabled: toggle,
});
describe('Overview - Fix deprecation logs step', () => {
let testBed: OverviewTestBed;
describe('ES deprecation logs', () => {
let testBed: EsDeprecationLogsTestBed;
const { server, httpRequestsMockHelpers } = setupEnvironment();
beforeEach(async () => {
httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(getLoggingResponse(true));
testBed = await setupOverviewPage();
const { component } = testBed;
component.update();
testBed = await setupESDeprecationLogsPage();
testBed.component.update();
});
afterAll(() => {
server.restore();
});
describe('Step status', () => {
test(`It's complete when there are no deprecation logs since last checkpoint`, async () => {
httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse({ count: 0 });
describe('Documentation link', () => {
test('Has a link for migration info api docs in page header', () => {
const { exists } = testBed;
await act(async () => {
testBed = await setupOverviewPage();
});
const { exists, component } = testBed;
component.update();
expect(exists(`fixLogsStep-complete`)).toBe(true);
});
test(`It's incomplete when there are deprecation logs since last checkpoint`, async () => {
httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse({ count: 5 });
await act(async () => {
testBed = await setupOverviewPage();
});
const { exists, component } = testBed;
component.update();
expect(exists(`fixLogsStep-incomplete`)).toBe(true);
});
test(`It's incomplete when log collection is disabled `, async () => {
httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse({ count: 0 });
await act(async () => {
testBed = await setupOverviewPage();
});
const { actions, exists, component } = testBed;
component.update();
expect(exists(`fixLogsStep-complete`)).toBe(true);
httpRequestsMockHelpers.setUpdateDeprecationLoggingResponse(getLoggingResponse(false));
await actions.clickDeprecationToggle();
expect(exists(`fixLogsStep-incomplete`)).toBe(true);
expect(exists('documentationLink')).toBe(true);
});
});
@ -123,7 +81,7 @@ describe('Overview - Fix deprecation logs step', () => {
});
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { exists, component } = testBed;
@ -159,7 +117,7 @@ describe('Overview - Fix deprecation logs step', () => {
httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, error);
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { component, exists } = testBed;
@ -176,7 +134,7 @@ describe('Overview - Fix deprecation logs step', () => {
});
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { exists, component } = testBed;
@ -196,7 +154,7 @@ describe('Overview - Fix deprecation logs step', () => {
test('Has a link to see logs in observability app', async () => {
await act(async () => {
testBed = await setupOverviewPage({
testBed = await setupESDeprecationLogsPage({
http: {
basePath: {
prepend: (url: string) => url,
@ -228,7 +186,7 @@ describe('Overview - Fix deprecation logs step', () => {
test('Has a link to see logs in discover app', async () => {
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { exists, component, find } = testBed;
@ -257,7 +215,7 @@ describe('Overview - Fix deprecation logs step', () => {
});
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { find, exists, component } = testBed;
@ -274,7 +232,7 @@ describe('Overview - Fix deprecation logs step', () => {
});
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { find, exists, component } = testBed;
@ -295,7 +253,7 @@ describe('Overview - Fix deprecation logs step', () => {
httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse(undefined, error);
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { exists, actions, component } = testBed;
@ -319,7 +277,7 @@ describe('Overview - Fix deprecation logs step', () => {
});
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { exists, actions, component } = testBed;
@ -351,7 +309,7 @@ describe('Overview - Fix deprecation logs step', () => {
const addDanger = jest.fn();
await act(async () => {
testBed = await setupOverviewPage({
testBed = await setupESDeprecationLogsPage({
services: {
core: {
notifications: {
@ -389,17 +347,17 @@ describe('Overview - Fix deprecation logs step', () => {
count: 0,
});
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
afterEach(() => {
jest.useRealTimers();
});
test('renders step as incomplete when a success state is followed by an error state', async () => {
test('success state is followed by an error state', async () => {
const { exists } = testBed;
expect(exists('fixLogsStep-complete')).toBe(true);
expect(exists('resetLastStoredDate')).toBe(true);
// second request will error
const error = {
@ -413,7 +371,7 @@ describe('Overview - Fix deprecation logs step', () => {
await advanceTime(DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS);
testBed.component.update();
expect(exists('fixLogsStep-incomplete')).toBe(true);
expect(exists('errorCallout')).toBe(true);
});
});
});
@ -425,7 +383,7 @@ describe('Overview - Fix deprecation logs step', () => {
test('It shows copy with compatibility api header advice', async () => {
await act(async () => {
testBed = await setupOverviewPage();
testBed = await setupESDeprecationLogsPage();
});
const { exists, component } = testBed;
@ -449,7 +407,7 @@ describe('Overview - Fix deprecation logs step', () => {
test(`doesn't show analyze and resolve logs if it doesn't have the right privileges`, async () => {
await act(async () => {
testBed = await setupOverviewPage({
testBed = await setupESDeprecationLogsPage({
privileges: {
hasAllPrivileges: false,
missingPrivileges: {

View file

@ -27,36 +27,6 @@ const createActions = (testBed: TestBed) => {
* User Actions
*/
const clickDeprecationToggle = async () => {
const { find, component } = testBed;
await act(async () => {
find('deprecationLoggingToggle').simulate('click');
});
component.update();
};
const clickRetryButton = async () => {
const { find, component } = testBed;
await act(async () => {
find('retryButton').simulate('click');
});
component.update();
};
const clickResetButton = async () => {
const { find, component } = testBed;
await act(async () => {
find('resetLastStoredDate').simulate('click');
});
component.update();
};
const clickViewSystemIndicesState = async () => {
const { find, component } = testBed;
@ -78,9 +48,6 @@ const createActions = (testBed: TestBed) => {
};
return {
clickDeprecationToggle,
clickRetryButton,
clickResetButton,
clickViewSystemIndicesState,
clickRetrySystemIndicesButton,
};

View file

@ -17,7 +17,13 @@ import { ClusterUpgradeState } from '../../common/types';
import { APP_WRAPPER_CLASS, GlobalFlyout, AuthorizationProvider } from '../shared_imports';
import { AppDependencies } from '../types';
import { AppContextProvider, useAppContext } from './app_context';
import { EsDeprecations, ComingSoonPrompt, KibanaDeprecations, Overview } from './components';
import {
EsDeprecations,
EsDeprecationLogs,
ComingSoonPrompt,
KibanaDeprecations,
Overview,
} from './components';
const { GlobalFlyoutProvider } = GlobalFlyout;
@ -112,6 +118,7 @@ const AppHandlingClusterUpgradeState: React.FunctionComponent = () => {
<Switch>
<Route exact path="/overview" component={Overview} />
<Route exact path="/es_deprecations" component={EsDeprecations} />
<Route exact path="/es_deprecation_logs" component={EsDeprecationLogs} />
<Route exact path="/kibana_deprecations" component={KibanaDeprecations} />
<Redirect from="/" to="/overview" />
</Switch>

View file

@ -0,0 +1,74 @@
/*
* 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, { FunctionComponent, useEffect } from 'react';
import {
EuiPageHeader,
EuiButtonEmpty,
EuiSpacer,
EuiPageBody,
EuiPageContentBody,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { FormattedMessage } from '@kbn/i18n/react';
import { useAppContext } from '../../app_context';
import { uiMetricService, UIM_ES_DEPRECATION_LOGS_PAGE_LOAD } from '../../lib/ui_metric';
import { FixDeprecationLogs } from './fix_deprecation_logs';
export const EsDeprecationLogs: FunctionComponent = () => {
const {
services: {
breadcrumbs,
core: { docLinks },
},
} = useAppContext();
useEffect(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_ES_DEPRECATION_LOGS_PAGE_LOAD);
}, []);
useEffect(() => {
breadcrumbs.setBreadcrumbs('esDeprecationLogs');
}, [breadcrumbs]);
return (
<EuiPageBody restrictWidth={true} data-test-subj="esDeprecationLogs">
<EuiPageContentBody color="transparent" paddingSize="none">
<EuiPageHeader
bottomBorder
pageTitle={i18n.translate('xpack.upgradeAssistant.esDeprecationLogs.pageTitle', {
defaultMessage: 'Elasticsearch deprecation logs',
})}
description={i18n.translate('xpack.upgradeAssistant.esDeprecationLogs.pageDescription', {
defaultMessage:
'Review the deprecation logs to determine if your applications are using any deprecated APIs. Update your applications to prevent errors or changes in behavior after you upgrade.',
})}
rightSideItems={[
<EuiButtonEmpty
href={docLinks.links.elasticsearch.migrationApiDeprecation}
target="_blank"
iconType="help"
data-test-subj="documentationLink"
>
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecationLogs.documentationLinkText"
defaultMessage="Documentation"
/>
</EuiButtonEmpty>,
]}
/>
<EuiSpacer size="l" />
<FixDeprecationLogs />
</EuiPageContentBody>
</EuiPageBody>
);
};

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { FunctionComponent, useEffect, useState } from 'react';
import React, { FunctionComponent, useState } from 'react';
import moment from 'moment-timezone';
import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
@ -54,13 +54,11 @@ const i18nTexts = {
interface Props {
checkpoint: string;
setCheckpoint: (value: string) => void;
setHasNoDeprecationLogs: (hasNoLogs: boolean) => void;
}
export const DeprecationsCountCheckpoint: FunctionComponent<Props> = ({
checkpoint,
setCheckpoint,
setHasNoDeprecationLogs,
}) => {
const [isDeletingCache, setIsDeletingCache] = useState(false);
const {
@ -96,16 +94,6 @@ export const DeprecationsCountCheckpoint: FunctionComponent<Props> = ({
setCheckpoint(now);
};
useEffect(() => {
// Loading shouldn't invalidate the previous state.
if (!isLoading) {
// An error should invalidate the previous state.
setHasNoDeprecationLogs(!error && !hasLogs);
}
// Depending upon setHasNoDeprecationLogs would create an infinite loop.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [error, isLoading, hasLogs]);
if (isInitialRequest && isLoading) {
return <EuiLoadingContent lines={6} />;
}

View file

@ -9,7 +9,6 @@ import React, { FunctionComponent, useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText, EuiSpacer, EuiLink, EuiCallOut, EuiCode } from '@elastic/eui';
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
import { useAppContext } from '../../../app_context';
import { ExternalLinks } from './external_links';
@ -17,14 +16,10 @@ import { DeprecationsCountCheckpoint } from './deprecations_count_checkpoint';
import { useDeprecationLogging } from './use_deprecation_logging';
import { DeprecationLoggingToggle } from './deprecation_logging_toggle';
import { loadLogsCheckpoint, saveLogsCheckpoint } from '../../../lib/logs_checkpoint';
import type { OverviewStepProps } from '../../types';
import { DEPRECATION_LOGS_INDEX } from '../../../../../common/constants';
import { WithPrivileges, MissingPrivileges } from '../../../../shared_imports';
const i18nTexts = {
identifyStepTitle: i18n.translate('xpack.upgradeAssistant.overview.identifyStepTitle', {
defaultMessage: 'Identify deprecated API use and update your applications',
}),
analyzeTitle: i18n.translate('xpack.upgradeAssistant.overview.analyzeTitle', {
defaultMessage: 'Analyze deprecation logs',
}),
@ -93,16 +88,11 @@ const i18nTexts = {
};
interface Props {
setIsComplete: OverviewStepProps['setIsComplete'];
hasPrivileges: boolean;
privilegesMissing: MissingPrivileges;
}
const FixLogsStep: FunctionComponent<Props> = ({
setIsComplete,
hasPrivileges,
privilegesMissing,
}) => {
const FixDeprecationLogsUI: FunctionComponent<Props> = ({ hasPrivileges, privilegesMissing }) => {
const {
services: {
core: { docLinks },
@ -126,15 +116,6 @@ const FixLogsStep: FunctionComponent<Props> = ({
saveLogsCheckpoint(checkpoint);
}, [checkpoint]);
useEffect(() => {
if (!isDeprecationLogIndexingEnabled) {
setIsComplete(false);
}
// Depending upon setIsComplete would create an infinite loop.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDeprecationLogIndexingEnabled]);
return (
<>
<DeprecationLoggingToggle
@ -189,11 +170,7 @@ const FixLogsStep: FunctionComponent<Props> = ({
<h4>{i18nTexts.deprecationsCountCheckpointTitle}</h4>
</EuiText>
<EuiSpacer size="m" />
<DeprecationsCountCheckpoint
checkpoint={checkpoint}
setCheckpoint={setCheckpoint}
setHasNoDeprecationLogs={setIsComplete}
/>
<DeprecationsCountCheckpoint checkpoint={checkpoint} setCheckpoint={setCheckpoint} />
<EuiSpacer size="xl" />
<EuiText data-test-subj="apiCompatibilityNoteTitle">
@ -213,23 +190,15 @@ const FixLogsStep: FunctionComponent<Props> = ({
);
};
export const getFixLogsStep = ({ isComplete, setIsComplete }: OverviewStepProps): EuiStepProps => {
const status = isComplete ? 'complete' : 'incomplete';
return {
status,
title: i18nTexts.identifyStepTitle,
'data-test-subj': `fixLogsStep-${status}`,
children: (
<WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
{({ hasPrivileges, privilegesMissing, isLoading }) => (
<FixLogsStep
setIsComplete={setIsComplete}
hasPrivileges={!isLoading && hasPrivileges}
privilegesMissing={privilegesMissing}
/>
)}
</WithPrivileges>
),
};
export const FixDeprecationLogs = () => {
return (
<WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
{({ hasPrivileges, privilegesMissing, isLoading }) => (
<FixDeprecationLogsUI
hasPrivileges={!isLoading && hasPrivileges}
privilegesMissing={privilegesMissing}
/>
)}
</WithPrivileges>
);
};

View file

@ -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 { FixDeprecationLogs } from './fix_deprecation_logs';

View file

@ -5,4 +5,4 @@
* 2.0.
*/
export { getFixLogsStep } from './fix_logs_step';
export { EsDeprecationLogs } from './es_deprecation_logs';

View file

@ -7,5 +7,6 @@
export { KibanaDeprecations } from './kibana_deprecations';
export { EsDeprecations } from './es_deprecations';
export { EsDeprecationLogs } from './es_deprecation_logs';
export { ComingSoonPrompt } from './coming_soon_prompt';
export { Overview } from './overview';

View file

@ -7,11 +7,13 @@
import React, { FunctionComponent, useState, useEffect } from 'react';
import { EuiText, EuiFlexItem, EuiFlexGroup, EuiSpacer } from '@elastic/eui';
import { EuiText, EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
import { DEPRECATION_LOGS_INDEX } from '../../../../../common/constants';
import { WithPrivileges } from '../../../../shared_imports';
import type { OverviewStepProps } from '../../types';
import { EsDeprecationIssuesPanel, KibanaDeprecationIssuesPanel } from './components';
@ -49,10 +51,48 @@ const FixIssuesStep: FunctionComponent<Props> = ({ setIsComplete }) => {
);
};
interface CustomProps {
navigateToEsDeprecationLogs: () => void;
}
const AccessDeprecationLogsMessage = ({ navigateToEsDeprecationLogs }: CustomProps) => {
return (
<WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
{({ hasPrivileges, isLoading }) => {
if (isLoading || !hasPrivileges) {
// Don't show the message with the link to access deprecation logs
// to users who can't access the UI anyways.
return null;
}
return (
<FormattedMessage
id="xpack.upgradeAssistant.overview.accessEsDeprecationLogsLabel"
defaultMessage="If you have application code that calls Elasticsearch APIs, review the {esDeprecationLogsLink} to make sure you are not using deprecated APIs."
values={{
esDeprecationLogsLink: (
<EuiLink
onClick={navigateToEsDeprecationLogs}
data-test-subj="viewElasticsearchDeprecationLogs"
>
{i18n.translate('xpack.upgradeAssistant.overview.esDeprecationLogsLink', {
defaultMessage: 'Elasticsearch deprecation logs',
})}
</EuiLink>
),
}}
/>
);
}}
</WithPrivileges>
);
};
export const getFixIssuesStep = ({
isComplete,
setIsComplete,
}: OverviewStepProps): EuiStepProps => {
navigateToEsDeprecationLogs,
}: OverviewStepProps & CustomProps): EuiStepProps => {
const status = isComplete ? 'complete' : 'incomplete';
return {
@ -65,7 +105,14 @@ export const getFixIssuesStep = ({
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overview.fixIssuesStepDescription"
defaultMessage="Update your Elasticsearch and Kibana deployments to be compatible with Elastic 8.x. Critical issues must be resolved before you upgrade. Warning issues can be ignored at your discretion."
defaultMessage="You must resolve any critical Elasticsearch and Kibana configuration issues before upgrading to Elastic 8.x. Ignoring warnings might result in differences in behavior after you upgrade. {accessDeprecationLogsMessage}"
values={{
accessDeprecationLogsMessage: (
<AccessDeprecationLogsMessage
navigateToEsDeprecationLogs={navigateToEsDeprecationLogs}
/>
),
}}
/>
</p>
</EuiText>

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { FunctionComponent, useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import {
EuiSteps,
@ -15,23 +15,23 @@ import {
EuiSpacer,
EuiLink,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { FormattedMessage } from '@kbn/i18n/react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useAppContext } from '../../app_context';
import { uiMetricService, UIM_OVERVIEW_PAGE_LOAD } from '../../lib/ui_metric';
import { getBackupStep } from './backup_step';
import { getFixIssuesStep } from './fix_issues_step';
import { getFixLogsStep } from './fix_logs_step';
import { getUpgradeStep } from './upgrade_step';
import { getMigrateSystemIndicesStep } from './migrate_system_indices';
type OverviewStep = 'backup' | 'migrate_system_indices' | 'fix_issues' | 'fix_logs';
type OverviewStep = 'backup' | 'migrate_system_indices' | 'fix_issues';
export const Overview: FunctionComponent = () => {
export const Overview = withRouter(({ history }: RouteComponentProps) => {
const {
services: {
breadcrumbs,
@ -52,7 +52,6 @@ export const Overview: FunctionComponent = () => {
backup: false,
migrate_system_indices: false,
fix_issues: false,
fix_logs: false,
});
const isStepComplete = (step: OverviewStep) => completedStepsMap[step];
@ -65,7 +64,7 @@ export const Overview: FunctionComponent = () => {
return (
<EuiPageBody restrictWidth={true} data-test-subj="overview">
<EuiPageContent horizontalPosition="center" color="transparent" paddingSize="none">
<EuiPageContentBody color="transparent" paddingSize="none">
<EuiPageHeader
bottomBorder
pageTitle={i18n.translate('xpack.upgradeAssistant.overview.pageTitle', {
@ -114,15 +113,12 @@ export const Overview: FunctionComponent = () => {
getFixIssuesStep({
isComplete: isStepComplete('fix_issues'),
setIsComplete: setCompletedStep.bind(null, 'fix_issues'),
}),
getFixLogsStep({
isComplete: isStepComplete('fix_logs'),
setIsComplete: setCompletedStep.bind(null, 'fix_logs'),
navigateToEsDeprecationLogs: () => history.push('/es_deprecation_logs'),
}),
getUpgradeStep(),
]}
/>
</EuiPageContent>
</EuiPageContentBody>
</EuiPageBody>
);
};
});

View file

@ -18,6 +18,9 @@ const i18nTexts = {
esDeprecations: i18n.translate('xpack.upgradeAssistant.breadcrumb.esDeprecationsLabel', {
defaultMessage: 'Elasticsearch deprecation issues',
}),
esDeprecationLogs: i18n.translate('xpack.upgradeAssistant.breadcrumb.esDeprecationLogsLabel', {
defaultMessage: 'Elasticsearch deprecation logs',
}),
kibanaDeprecations: i18n.translate(
'xpack.upgradeAssistant.breadcrumb.kibanaDeprecationsLabel',
{
@ -48,6 +51,15 @@ export class BreadcrumbService {
text: i18nTexts.breadcrumbs.esDeprecations,
},
],
esDeprecationLogs: [
{
text: i18nTexts.breadcrumbs.overview,
href: '/',
},
{
text: i18nTexts.breadcrumbs.esDeprecationLogs,
},
],
kibanaDeprecations: [
{
text: i18nTexts.breadcrumbs.overview,
@ -65,7 +77,9 @@ export class BreadcrumbService {
this.setBreadcrumbsHandler = setBreadcrumbsHandler;
}
public setBreadcrumbs(type: 'overview' | 'esDeprecations' | 'kibanaDeprecations'): void {
public setBreadcrumbs(
type: 'overview' | 'esDeprecations' | 'esDeprecationLogs' | 'kibanaDeprecations'
): void {
if (!this.setBreadcrumbsHandler) {
throw new Error('Breadcrumb service has not been initialized');
}

View file

@ -12,6 +12,7 @@ export const UIM_APP_NAME = 'upgrade_assistant';
export const UIM_ES_DEPRECATIONS_PAGE_LOAD = 'es_deprecations_page_load';
export const UIM_KIBANA_DEPRECATIONS_PAGE_LOAD = 'kibana_deprecations_page_load';
export const UIM_OVERVIEW_PAGE_LOAD = 'overview_page_load';
export const UIM_ES_DEPRECATION_LOGS_PAGE_LOAD = 'es_deprecation_logs_page_load';
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';

View file

@ -5,10 +5,10 @@
* 2.0.
*/
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { IndicesCreateRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { FtrProviderContext } from '../ftr_provider_context';
const translogSettingsIndexDeprecation: estypes.IndicesCreateRequest = {
const translogSettingsIndexDeprecation: IndicesCreateRequest = {
index: 'deprecated_settings',
body: {
settings: {
@ -19,7 +19,7 @@ const translogSettingsIndexDeprecation: estypes.IndicesCreateRequest = {
},
};
const multiFieldsIndexDeprecation: estypes.IndicesCreateRequest = {
const multiFieldsIndexDeprecation: IndicesCreateRequest = {
index: 'nested_multi_fields',
body: {
mappings: {
@ -55,6 +55,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe.skip('Upgrade Assistant', () => {
before(async () => {
await PageObjects.upgradeAssistant.navigateToPage();
try {
// Create two indices that will trigger deprecation warnings to test the ES deprecations page
await es.indices.create(multiFieldsIndexDeprecation);
@ -76,128 +77,113 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
});
describe('Upgrade Assistant - Overview', () => {
before(async () => {
describe('Overview page', () => {
beforeEach(async () => {
await PageObjects.upgradeAssistant.navigateToPage();
try {
// Create two indices that will trigger deprecation warnings to test the ES deprecations page
await es.indices.create(multiFieldsIndexDeprecation);
await es.indices.create(translogSettingsIndexDeprecation);
} catch (e) {
log.debug('[Setup error] Error creating indices');
throw e;
}
});
after(async () => {
try {
await es.indices.delete({
index: [multiFieldsIndexDeprecation.index, translogSettingsIndexDeprecation.index],
});
} catch (e) {
log.debug('[Cleanup error] Error deleting indices');
throw e;
}
});
describe('Overview page', () => {
beforeEach(async () => {
await PageObjects.upgradeAssistant.navigateToPage();
await retry.waitFor('Upgrade Assistant overview page to be visible', async () => {
return testSubjects.exists('overview');
});
});
it('with logs collection disabled', async () => {
await a11y.testAppSnapshot();
});
it('with logs collection enabled', async () => {
await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle();
await retry.waitFor('UA external links title to be present', async () => {
return testSubjects.isDisplayed('externalLinksTitle');
});
await a11y.testAppSnapshot();
await retry.waitFor('Upgrade Assistant overview page to be visible', async () => {
return testSubjects.exists('overview');
});
});
describe('Elasticsearch deprecations page', () => {
beforeEach(async () => {
await PageObjects.common.navigateToUrl(
'management',
'stack/upgrade_assistant/es_deprecations',
{
ensureCurrentUrl: false,
shouldLoginIfPrompted: false,
shouldUseHashForSubUrl: false,
}
);
it('has no accessibility issues', async () => {
await a11y.testAppSnapshot();
});
});
await retry.waitFor('Elasticsearch deprecations table to be visible', async () => {
return testSubjects.exists('esDeprecationsTable');
});
describe('ES deprecations logs page', () => {
beforeEach(async () => {
await PageObjects.upgradeAssistant.navigateToEsDeprecationLogs();
});
it('with logs collection disabled', async () => {
await a11y.testAppSnapshot();
});
it('with logs collection enabled', async () => {
await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle();
await retry.waitFor('UA external links title to be present', async () => {
return testSubjects.isDisplayed('externalLinksTitle');
});
it('Deprecations table', async () => {
await a11y.testAppSnapshot();
});
await a11y.testAppSnapshot();
});
});
it('Index settings deprecation flyout', async () => {
await PageObjects.upgradeAssistant.clickEsDeprecation(
'indexSettings' // An index setting deprecation was added in the before() hook so should be guaranteed
);
await retry.waitFor('ES index settings deprecation flyout to be visible', async () => {
return testSubjects.exists('indexSettingsDetails');
});
await a11y.testAppSnapshot();
});
describe('Elasticsearch deprecations page', () => {
beforeEach(async () => {
await PageObjects.common.navigateToUrl(
'management',
'stack/upgrade_assistant/es_deprecations',
{
ensureCurrentUrl: false,
shouldLoginIfPrompted: false,
shouldUseHashForSubUrl: false,
}
);
it('Default deprecation flyout', async () => {
await PageObjects.upgradeAssistant.clickEsDeprecation(
'default' // A default deprecation was added in the before() hook so should be guaranteed
);
await retry.waitFor('ES default deprecation flyout to be visible', async () => {
return testSubjects.exists('defaultDeprecationDetails');
});
await a11y.testAppSnapshot();
await retry.waitFor('Elasticsearch deprecations table to be visible', async () => {
return testSubjects.exists('esDeprecationsTable');
});
});
describe('Kibana deprecations page', () => {
beforeEach(async () => {
await PageObjects.common.navigateToUrl(
'management',
'stack/upgrade_assistant/kibana_deprecations',
{
ensureCurrentUrl: false,
shouldLoginIfPrompted: false,
shouldUseHashForSubUrl: false,
}
);
it('Deprecations table', async () => {
await a11y.testAppSnapshot();
});
await retry.waitFor('Kibana deprecations to be visible', async () => {
return testSubjects.exists('kibanaDeprecations');
});
// Failing: See https://github.com/elastic/kibana/issues/115859
it.skip('Index settings deprecation flyout', async () => {
await PageObjects.upgradeAssistant.clickEsDeprecation(
'indexSettings' // An index setting deprecation was added in the before() hook so should be guaranteed
);
await retry.waitFor('ES index settings deprecation flyout to be visible', async () => {
return testSubjects.exists('indexSettingsDetails');
});
await a11y.testAppSnapshot();
});
it('Default deprecation flyout', async () => {
await PageObjects.upgradeAssistant.clickEsDeprecation(
'default' // A default deprecation was added in the before() hook so should be guaranteed
);
await retry.waitFor('ES default deprecation flyout to be visible', async () => {
return testSubjects.exists('defaultDeprecationDetails');
});
await a11y.testAppSnapshot();
});
});
describe('Kibana deprecations page', () => {
beforeEach(async () => {
await PageObjects.common.navigateToUrl(
'management',
'stack/upgrade_assistant/kibana_deprecations',
{
ensureCurrentUrl: false,
shouldLoginIfPrompted: false,
shouldUseHashForSubUrl: false,
}
);
await retry.waitFor('Kibana deprecations to be visible', async () => {
return testSubjects.exists('kibanaDeprecations');
});
});
it('Deprecations table', async () => {
await a11y.testAppSnapshot();
});
it('Deprecation details flyout', async () => {
await PageObjects.upgradeAssistant.clickKibanaDeprecation(
'xpack.securitySolution has a deprecated setting' // This deprecation was added to the test runner config so should be guaranteed
);
await retry.waitFor('Kibana deprecation details flyout to be visible', async () => {
return testSubjects.exists('kibanaDeprecationDetails');
});
it('Deprecations table', async () => {
await a11y.testAppSnapshot();
});
it('Deprecation details flyout', async () => {
await PageObjects.upgradeAssistant.clickKibanaDeprecation(
'xpack.securitySolution has a deprecated setting' // This deprecation was added to the test runner config so should be guaranteed
);
await retry.waitFor('Kibana deprecation details flyout to be visible', async () => {
return testSubjects.exists('kibanaDeprecationDetails');
});
await a11y.testAppSnapshot();
});
await a11y.testAppSnapshot();
});
});
});

View file

@ -0,0 +1,45 @@
/*
* 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 { FtrProviderContext } from '../../ftr_provider_context';
export default function upgradeAssistantESDeprecationLogsPageFunctionalTests({
getService,
getPageObjects,
}: FtrProviderContext) {
const PageObjects = getPageObjects(['upgradeAssistant', 'common']);
const security = getService('security');
const testSubjects = getService('testSubjects');
const es = getService('es');
describe.skip('ES deprecation logs page', function () {
this.tags('skipFirefox');
before(async () => {
await security.testUser.setRoles(['superuser']);
// Access to system indices will be deprecated and should generate a deprecation log
await es.indices.get({ index: '.kibana' });
});
after(async () => {
await security.testUser.restoreDefaults();
});
beforeEach(async () => {
await PageObjects.upgradeAssistant.navigateToEsDeprecationLogs();
});
it('Shows warnings callout if there are deprecations', async () => {
testSubjects.exists('hasWarningsCallout');
});
it('Shows no warnings callout if there are no deprecations', async () => {
await PageObjects.upgradeAssistant.clickResetLastCheckpointButton();
testSubjects.exists('noWarningsCallout');
});
});
}

View file

@ -14,5 +14,6 @@ export default function upgradeCheckup({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./feature_controls'));
loadTestFile(require.resolve('./deprecation_pages'));
loadTestFile(require.resolve('./overview_page'));
loadTestFile(require.resolve('./es_deprecation_logs_page'));
});
}

View file

@ -15,7 +15,6 @@ export default function upgradeAssistantOverviewPageFunctionalTests({
const retry = getService('retry');
const security = getService('security');
const testSubjects = getService('testSubjects');
const es = getService('es');
describe.skip('Overview Page', function () {
this.tags('skipFirefox');
@ -41,37 +40,7 @@ export default function upgradeAssistantOverviewPageFunctionalTests({
it('Should render all steps', async () => {
testSubjects.exists('backupStep-incomplete');
testSubjects.exists('fixIssuesStep-incomplete');
testSubjects.exists('fixLogsStep-incomplete');
testSubjects.exists('upgradeStep');
});
describe('fixLogsStep', () => {
before(async () => {
await PageObjects.upgradeAssistant.navigateToPage();
// Access to system indices will be deprecated and should generate a deprecation log
await es.indices.get({ index: '.kibana' });
// Only click deprecation logging toggle if its not already enabled
if (!(await testSubjects.isDisplayed('externalLinksTitle'))) {
await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle();
}
await retry.waitFor('UA external links title to be present', async () => {
return testSubjects.isDisplayed('externalLinksTitle');
});
});
beforeEach(async () => {
await PageObjects.upgradeAssistant.navigateToPage();
});
it('Shows warnings callout if there are deprecations', async () => {
testSubjects.exists('hasWarningsCallout');
});
it('Shows no warnings callout if there are no deprecations', async () => {
await PageObjects.upgradeAssistant.clickResetLastCheckpointButton();
testSubjects.exists('noWarningsCallout');
});
});
});
}

View file

@ -29,6 +29,21 @@ export class UpgradeAssistantPageObject extends FtrService {
});
}
async navigateToEsDeprecationLogs() {
return await this.retry.try(async () => {
await this.common.navigateToApp('settings');
await this.testSubjects.click('upgrade_assistant');
await this.testSubjects.click('viewElasticsearchDeprecationLogs');
await this.retry.waitFor(
'url to contain /upgrade_assistant/es_deprecation_logs',
async () => {
const url = await this.browser.getCurrentUrl();
return url.includes('/es_deprecation_logs');
}
);
});
}
async clickEsDeprecationsPanel() {
return await this.retry.try(async () => {
await this.testSubjects.click('esStatsPanel');