Skip to content

Commit

Permalink
[Upgrade Assistant] Check for ML upgrade mode before enabling flyout …
Browse files Browse the repository at this point in the history
…actions (#112555)
  • Loading branch information
sabarasaba authored Sep 27, 2021
1 parent 05210e0 commit b409e2e
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ export class DocLinksService {
outlierDetectionRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-roc`,
regressionEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-regression-evaluation`,
classificationAucRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-class-aucroc`,
setUpgradeMode: `${ELASTICSEARCH_DOCS}ml-set-upgrade-mode.html`,
},
transforms: {
guide: `${ELASTICSEARCH_DOCS}transforms.html`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,23 @@ describe('ES deprecations table', () => {
const mlDeprecation = esDeprecationsMockResponse.deprecations[0];
const reindexDeprecation = esDeprecationsMockResponse.deprecations[3];

// Since upgradeStatusMockResponse includes ML and reindex actions (which require fetching status), there will be 3 requests made
expect(server.requests.length).toBe(totalRequests + 3);
expect(server.requests[server.requests.length - 3].url).toBe(
// Since upgradeStatusMockResponse includes ML and reindex actions (which require fetching status), there will be 4 requests made
expect(server.requests.length).toBe(totalRequests + 4);
expect(server.requests[server.requests.length - 4].url).toBe(
`${API_BASE_PATH}/es_deprecations`
);
expect(server.requests[server.requests.length - 2].url).toBe(
expect(server.requests[server.requests.length - 3].url).toBe(
`${API_BASE_PATH}/ml_snapshots/${(mlDeprecation.correctiveAction as MlAction).jobId}/${
(mlDeprecation.correctiveAction as MlAction).snapshotId
}`
);
expect(server.requests[server.requests.length - 1].url).toBe(
expect(server.requests[server.requests.length - 2].url).toBe(
`${API_BASE_PATH}/reindex/${reindexDeprecation.index}`
);

expect(server.requests[server.requests.length - 1].url).toBe(
`${API_BASE_PATH}/ml_upgrade_mode`
);
});

it('shows critical and warning deprecations count', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('Machine learning deprecation flyout', () => {

beforeEach(async () => {
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse);
httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ mlUpgradeModeEnabled: false });
httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({
nodeId: 'my_node',
snapshotId: MOCK_SNAPSHOT_ID,
Expand Down Expand Up @@ -131,6 +132,27 @@ describe('Machine learning deprecation flyout', () => {
// Verify the upgrade button text changes
expect(find('mlSnapshotDetails.upgradeSnapshotButton').text()).toEqual('Retry upgrade');
});

it('Disables actions if ml_upgrade_mode is enabled', async () => {
httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ mlUpgradeModeEnabled: true });

await act(async () => {
testBed = await setupElasticsearchPage({ isReadOnlyMode: false });
});

const { actions, exists, component } = testBed;

component.update();

await actions.table.clickDeprecationRowAt('mlSnapshot', 0);

// Shows an error callout with a docs link
expect(exists('mlSnapshotDetails.mlUpgradeModeEnabledError')).toBe(true);
expect(exists('mlSnapshotDetails.setUpgradeModeDocsLink')).toBe(true);
// Flyout actions should be hidden
expect(exists('mlSnapshotDetails.upgradeSnapshotButton')).toBe(false);
expect(exists('mlSnapshotDetails.deleteSnapshotButton')).toBe(false);
});
});

describe('delete snapshots', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
]);
};

const setLoadMlUpgradeModeResponse = (response?: object, error?: ResponseError) => {
const status = error ? error.statusCode || 400 : 200;
const body = error ? error : response;

server.respondWith('GET', `${API_BASE_PATH}/ml_upgrade_mode`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(body),
]);
};

return {
setLoadCloudBackupStatusResponse,
setLoadEsDeprecationsResponse,
Expand All @@ -136,6 +147,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
setDeleteMlSnapshotResponse,
setUpgradeMlSnapshotStatusResponse,
setLoadDeprecationLogsCountResponse,
setLoadMlUpgradeModeResponse,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useSnapshotState, SnapshotState } from './use_snapshot_state';

export interface MlSnapshotContext {
snapshotState: SnapshotState;
mlUpgradeModeEnabled: boolean;
upgradeSnapshot: () => Promise<void>;
deleteSnapshot: () => Promise<void>;
}
Expand All @@ -31,12 +32,14 @@ interface Props {
children: React.ReactNode;
snapshotId: string;
jobId: string;
mlUpgradeModeEnabled: boolean;
}

export const MlSnapshotsStatusProvider: React.FunctionComponent<Props> = ({
api,
snapshotId,
jobId,
mlUpgradeModeEnabled,
children,
}) => {
const { updateSnapshotStatus, snapshotState, upgradeSnapshot, deleteSnapshot } = useSnapshotState(
Expand All @@ -57,6 +60,7 @@ export const MlSnapshotsStatusProvider: React.FunctionComponent<Props> = ({
snapshotState,
upgradeSnapshot,
deleteSnapshot,
mlUpgradeModeEnabled,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';

import {
Expand All @@ -32,6 +33,7 @@ import {
} from '../../../../lib/ui_metric';
import { DeprecationBadge } from '../../../shared';
import { MlSnapshotContext } from './context';
import { useAppContext } from '../../../../app_context';
import { SnapshotState } from './use_snapshot_state';

export interface FixSnapshotsFlyoutProps extends MlSnapshotContext {
Expand Down Expand Up @@ -103,6 +105,28 @@ const i18nTexts = {
defaultMessage: 'Learn more about this deprecation',
}
),
upgradeModeEnabledErrorTitle: i18n.translate(
'xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradeModeEnabledErrorTitle',
{
defaultMessage: 'Machine Learning upgrade mode is enabled',
}
),
upgradeModeEnabledErrorDescription: (docsLink: string) => (
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradeModeEnabledErrorDescription"
defaultMessage="No actions can be taken on Machine Learning snapshots while upgrade mode is enabled. {docsLink}."
values={{
docsLink: (
<EuiLink href={docsLink} target="_blank" data-test-subj="setUpgradeModeDocsLink">
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.mlSnapshots.upgradeModeEnabledDocsLink"
defaultMessage="Learn more"
/>
</EuiLink>
),
}}
/>
),
};

const getDeleteButtonLabel = (snapshotState: SnapshotState) => {
Expand Down Expand Up @@ -145,7 +169,13 @@ export const FixSnapshotsFlyout = ({
snapshotState,
upgradeSnapshot,
deleteSnapshot,
mlUpgradeModeEnabled,
}: FixSnapshotsFlyoutProps) => {
const {
services: {
core: { docLinks },
},
} = useAppContext();
const isResolved = snapshotState.status === 'complete';

const onUpgradeSnapshot = () => {
Expand Down Expand Up @@ -187,6 +217,23 @@ export const FixSnapshotsFlyout = ({
<EuiSpacer />
</>
)}

{mlUpgradeModeEnabled && (
<>
<EuiCallOut
title={i18nTexts.upgradeModeEnabledErrorTitle}
color="warning"
iconType="alert"
data-test-subj="mlUpgradeModeEnabledError"
>
<p>
{i18nTexts.upgradeModeEnabledErrorDescription(docLinks.links.ml.setUpgradeMode)}
</p>
</EuiCallOut>
<EuiSpacer />
</>
)}

<EuiText>
<p>{deprecation.details}</p>
<p>
Expand All @@ -204,7 +251,7 @@ export const FixSnapshotsFlyout = ({
</EuiButtonEmpty>
</EuiFlexItem>

{!isResolved && (
{!isResolved && !mlUpgradeModeEnabled && (
<EuiFlexItem grow={false}>
<EuiFlexGroup>
<EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const { useGlobalFlyout } = GlobalFlyout;
interface TableRowProps {
deprecation: EnrichedDeprecationInfo;
rowFieldNames: DeprecationTableColumns[];
mlUpgradeModeEnabled: boolean;
}

export const MlSnapshotsTableRowCells: React.FunctionComponent<TableRowProps> = ({
Expand Down Expand Up @@ -85,6 +86,7 @@ export const MlSnapshotsTableRow: React.FunctionComponent<TableRowProps> = (prop
<MlSnapshotsStatusProvider
snapshotId={(props.deprecation.correctiveAction as MlAction).snapshotId}
jobId={(props.deprecation.correctiveAction as MlAction).jobId}
mlUpgradeModeEnabled={props.mlUpgradeModeEnabled}
api={api}
>
<MlSnapshotsTableRowCells {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
Query,
} from '@elastic/eui';
import { EnrichedDeprecationInfo } from '../../../../common/types';
import { useAppContext } from '../../app_context';
import {
MlSnapshotsTableRow,
DefaultTableRow,
Expand Down Expand Up @@ -101,10 +102,19 @@ const cellToLabelMap = {
const cellTypes = Object.keys(cellToLabelMap) as DeprecationTableColumns[];
const pageSizeOptions = PAGINATION_CONFIG.pageSizeOptions;

const renderTableRowCells = (deprecation: EnrichedDeprecationInfo) => {
const renderTableRowCells = (
deprecation: EnrichedDeprecationInfo,
mlUpgradeModeEnabled: boolean
) => {
switch (deprecation.correctiveAction?.type) {
case 'mlSnapshot':
return <MlSnapshotsTableRow deprecation={deprecation} rowFieldNames={cellTypes} />;
return (
<MlSnapshotsTableRow
deprecation={deprecation}
rowFieldNames={cellTypes}
mlUpgradeModeEnabled={mlUpgradeModeEnabled}
/>
);

case 'indexSetting':
return <IndexSettingsTableRow deprecation={deprecation} rowFieldNames={cellTypes} />;
Expand Down Expand Up @@ -146,6 +156,13 @@ export const EsDeprecationsTable: React.FunctionComponent<Props> = ({
deprecations = [],
reload,
}) => {
const {
services: { api },
} = useAppContext();

const { data } = api.useLoadMlUpgradeMode();
const mlUpgradeModeEnabled = !!data?.mlUpgradeModeEnabled;

const [sortConfig, setSortConfig] = useState<SortConfig>({
isSortAscending: true,
sortField: 'isCritical',
Expand Down Expand Up @@ -291,7 +308,7 @@ export const EsDeprecationsTable: React.FunctionComponent<Props> = ({
{visibleDeprecations.map((deprecation, index) => {
return (
<EuiTableRow data-test-subj="deprecationTableRow" key={`deprecation-row-${index}`}>
{renderTableRowCells(deprecation)}
{renderTableRowCells(deprecation, mlUpgradeModeEnabled)}
</EuiTableRow>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,15 @@ 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();
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,28 @@ describe('ML snapshots APIs', () => {
});
});

describe('GET /api/upgrade_assistant/ml_upgrade_mode', () => {
it('Retrieves ml upgrade mode', async () => {
(
routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.ml.info as jest.Mock
).mockResolvedValue({
body: {
upgrade_mode: true,
},
});

const resp = await routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/ml_upgrade_mode',
})(routeHandlerContextMock, createRequestMock({}), kibanaResponseFactory);

expect(resp.status).toEqual(200);
expect(resp.payload).toEqual({
mlUpgradeModeEnabled: true,
});
});
});

describe('GET /api/upgrade_assistant/ml_snapshots/:jobId/:snapshotId', () => {
it('returns "idle" status if saved object does not exist', async () => {
(
Expand Down
31 changes: 31 additions & 0 deletions x-pack/plugins/upgrade_assistant/server/routes/ml_snapshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,37 @@ export function registerMlSnapshotRoutes({ router, lib: { handleEsError } }: Rou
)
);

// Get the ml upgrade mode
router.get(
{
path: `${API_BASE_PATH}/ml_upgrade_mode`,
validate: false,
},
versionCheckHandlerWrapper(
async (
{
core: {
elasticsearch: { client: esClient },
},
},
request,
response
) => {
try {
const { body: mlInfo } = await esClient.asCurrentUser.ml.info();

return response.ok({
body: {
mlUpgradeModeEnabled: mlInfo.upgrade_mode,
},
});
} catch (e) {
return handleEsError({ error: e, response });
}
}
)
);

// Delete ML model snapshot
router.delete(
{
Expand Down

0 comments on commit b409e2e

Please sign in to comment.