[ML] Fixing management app race condition (#100785)

* [ML] Fixing management app race condition

* updating test id

* translation id

* adding link to license page

* fixing tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
James Gowdy 2021-05-28 21:23:10 +01:00 committed by GitHub
parent 0bcd78b0e9
commit e93c18fea7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 12 deletions

View file

@ -25,7 +25,7 @@ export function checkGetManagementMlJobsResolver() {
if (isManageML === true && isPlatinumOrTrialLicense === true) {
return resolve({ mlFeatureEnabledInSpace });
} else {
return reject();
return reject({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace });
}
})
.catch((e) => {

View file

@ -31,8 +31,8 @@ export const AccessDeniedPage = () => (
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.management.jobsList.accessDeniedTitle"
defaultMessage="Access denied"
id="xpack.ml.management.jobsList.insufficientLicenseTitle"
defaultMessage="Machine Learning"
/>
</h1>
</EuiTitle>
@ -42,7 +42,7 @@ export const AccessDeniedPage = () => (
<EuiSpacer size="m" />
<EuiCallOut
title={i18n.translate('xpack.ml.management.jobsList.noPermissionToAccessLabel', {
defaultMessage: 'You need permission to access ML jobs',
defaultMessage: 'Access denied',
})}
color="danger"
iconType="cross"

View file

@ -0,0 +1,82 @@
/*
* 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, { FC, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { CoreStart } from 'kibana/public';
import {
EuiCallOut,
EuiPage,
EuiPageBody,
EuiPageContentBody,
EuiPageContentHeader,
EuiPageContentHeaderSection,
EuiSpacer,
EuiText,
EuiTitle,
EuiLink,
} from '@elastic/eui';
interface Props {
basePath: CoreStart['http']['basePath'];
}
export const InsufficientLicensePage: FC<Props> = ({ basePath }) => (
<Fragment>
<EuiPage data-test-subj="mlPageInsufficientLicense">
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.management.jobsList.insufficientLicenseTitle"
defaultMessage="Machine Learning"
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="m" />
<EuiCallOut
title={i18n.translate('xpack.ml.management.jobsList.insufficientLicenseLabel', {
defaultMessage:
'Machine Learning is only available on a trial, platinum or enterprise license',
})}
color="danger"
iconType="cross"
>
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.ml.management.jobsList.insufficientLicenseDescription"
defaultMessage="Please {link} to use Machine Learning features"
values={{
link: (
<EuiLink
href={`${basePath.get()}/app/management/stack/license_management/home`}
>
<FormattedMessage
id="xpack.ml.management.jobsList.insufficientLicenseDescription.link"
defaultMessage="upgrade your license or start a trial"
/>
</EuiLink>
),
}}
/>
</p>
</EuiText>
</EuiCallOut>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
</Fragment>
);

View file

@ -38,6 +38,7 @@ import { getDocLinks } from '../../../../util/dependency_cache';
import { JobsListView } from '../../../../jobs/jobs_list/components/jobs_list_view/index';
import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/analytics_management/components/analytics_list';
import { AccessDeniedPage } from '../access_denied_page';
import { InsufficientLicensePage } from '../insufficient_license_page';
import { SharePluginStart } from '../../../../../../../../../src/plugins/share/public';
import type { SpacesPluginStart } from '../../../../../../../spaces/public';
import { JobSpacesSyncFlyout } from '../../../../components/job_spaces_sync';
@ -128,6 +129,7 @@ export const JobsListPage: FC<{
const spacesEnabled = spacesApi !== undefined;
const [initialized, setInitialized] = useState(false);
const [accessDenied, setAccessDenied] = useState(false);
const [isPlatinumOrTrialLicense, setIsPlatinumOrTrialLicense] = useState(true);
const [showSyncFlyout, setShowSyncFlyout] = useState(false);
const [isMlEnabledInSpace, setIsMlEnabledInSpace] = useState(false);
const tabs = useTabs(isMlEnabledInSpace, spacesApi);
@ -139,7 +141,11 @@ export const JobsListPage: FC<{
const { mlFeatureEnabledInSpace } = await checkGetManagementMlJobsResolver();
setIsMlEnabledInSpace(mlFeatureEnabledInSpace);
} catch (e) {
setAccessDenied(true);
if (e.mlFeatureEnabledInSpace && e.isPlatinumOrTrialLicense === false) {
setIsPlatinumOrTrialLicense(false);
} else {
setAccessDenied(true);
}
}
setInitialized(true);
};
@ -191,6 +197,10 @@ export const JobsListPage: FC<{
return <AccessDeniedPage />;
}
if (isPlatinumOrTrialLicense === false) {
return <InsufficientLicensePage basePath={coreStart.http.basePath} />;
}
return (
<RedirectAppLinks application={coreStart.application}>
<I18nContext>

View file

@ -53,6 +53,7 @@ import {
} from '../../triggers_actions_ui/public';
import { FileDataVisualizerPluginStart } from '../../file_data_visualizer/public';
import { PluginSetupContract as AlertingSetup } from '../../alerting/public';
import { registerManagementSection } from './application/management';
export interface MlStartDependencies {
data: DataPublicPluginStart;
@ -133,6 +134,10 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
this.urlGenerator = registerUrlGenerator(pluginsSetup.share, core);
}
if (pluginsSetup.management) {
registerManagementSection(pluginsSetup.management, core).enable();
}
const licensing = pluginsSetup.licensing.license$.pipe(take(1));
licensing.subscribe(async (license) => {
const [coreStart] = await core.getStartServices();
@ -160,7 +165,6 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
// note including registerFeature in register_helper would cause the page bundle size to increase significantly
const {
registerEmbeddables,
registerManagementSection,
registerMlUiActions,
registerSearchLinks,
registerMlAlerts,
@ -172,11 +176,6 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
registerSearchLinks(this.appUpdater$, fullLicense);
if (fullLicense) {
const canManageMLJobs =
capabilities.management?.insightsAndAlerting?.jobsListLink ?? false;
if (canManageMLJobs && pluginsSetup.management !== undefined) {
registerManagementSection(pluginsSetup.management, core).enable();
}
registerEmbeddables(pluginsSetup.embeddable, core);
registerMlUiActions(pluginsSetup.uiActions, core);

View file

@ -145,6 +145,15 @@ export function MachineLearningNavigationProvider({
});
},
async navigateToStackManagementInsuficientLicensePage() {
// clicks the jobsListLink and loads the jobs list page
await testSubjects.click('jobsListLink');
await retry.tryForTime(60 * 1000, async () => {
// verify that the overall page is present
await testSubjects.existOrFail('mlPageInsufficientLicense');
});
},
async navigateToStackManagementJobsListPageAnalyticsTab() {
// clicks the `Analytics` tab and loads the analytics list page
await testSubjects.click('mlStackManagementJobsListAnalyticsTab');

View file

@ -166,7 +166,12 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep(
'should load the stack management with the ML menu item being absent'
);
await ml.navigation.navigateToStackManagement({ expectMlLink: false });
await ml.navigation.navigateToStackManagement();
await ml.testExecution.logTestStep(
'should load the stack management with insufficient license warning'
);
await ml.navigation.navigateToStackManagementInsuficientLicensePage();
});
});
}