mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.8`: - [[ML] Disable delete action for deployed models (#158533)](https://github.com/elastic/kibana/pull/158533) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Dima Arnautov","email":"dmitrii.arnautov@elastic.co"},"sourceCommit":{"committedDate":"2023-05-26T15:17:38Z","message":"[ML] Disable delete action for deployed models (#158533)\n\n## Summary\r\n\r\nFixes https://github.com/elastic/kibana/issues/157692.\r\n\r\nDisables delete action for deployed models.\r\n\r\n<img width=\"1506\" alt=\"image\"\r\nsrc=\"8e44dcca
-789e-41c3-9dca-87034f84390b\">\r\n\r\n\r\n### Checklist\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios","sha":"59b1ba976720e69588ef2abad92b65fa523f6b9a","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix",":ml","Feature:3rd Party Models","Team:ML","v8.9.0","v8.8.1"],"number":158533,"url":"https://github.com/elastic/kibana/pull/158533","mergeCommit":{"message":"[ML] Disable delete action for deployed models (#158533)\n\n## Summary\r\n\r\nFixes https://github.com/elastic/kibana/issues/157692.\r\n\r\nDisables delete action for deployed models.\r\n\r\n<img width=\"1506\" alt=\"image\"\r\nsrc=\"8e44dcca
-789e-41c3-9dca-87034f84390b\">\r\n\r\n\r\n### Checklist\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios","sha":"59b1ba976720e69588ef2abad92b65fa523f6b9a"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/158533","number":158533,"mergeCommit":{"message":"[ML] Disable delete action for deployed models (#158533)\n\n## Summary\r\n\r\nFixes https://github.com/elastic/kibana/issues/157692.\r\n\r\nDisables delete action for deployed models.\r\n\r\n<img width=\"1506\" alt=\"image\"\r\nsrc=\"8e44dcca
-789e-41c3-9dca-87034f84390b\">\r\n\r\n\r\n### Checklist\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios","sha":"59b1ba976720e69588ef2abad92b65fa523f6b9a"}},{"branch":"8.8","label":"v8.8.1","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Dima Arnautov <dmitrii.arnautov@elastic.co>
This commit is contained in:
parent
eabb7daf21
commit
2940ec9f31
5 changed files with 54 additions and 13 deletions
|
@ -68,3 +68,5 @@ export const MODEL_STATE = {
|
|||
defaultMessage: 'downloaded',
|
||||
}),
|
||||
} as const;
|
||||
|
||||
export type ModelState = typeof MODEL_STATE[keyof typeof MODEL_STATE];
|
||||
|
|
|
@ -392,16 +392,24 @@ export function useModelActions({
|
|||
},
|
||||
{
|
||||
name: (model) => {
|
||||
const enabled = !isPopulatedObject(model.pipelines);
|
||||
const hasPipelines = isPopulatedObject(model.pipelines);
|
||||
const hasDeployments = model.state === MODEL_STATE.STARTED;
|
||||
return (
|
||||
<EuiToolTip
|
||||
position="left"
|
||||
content={
|
||||
enabled
|
||||
? null
|
||||
: i18n.translate('xpack.ml.trainedModels.modelsList.deleteDisabledTooltip', {
|
||||
hasPipelines
|
||||
? i18n.translate('xpack.ml.trainedModels.modelsList.deleteDisabledTooltip', {
|
||||
defaultMessage: 'Model has associated pipelines',
|
||||
})
|
||||
: hasDeployments
|
||||
? i18n.translate(
|
||||
'xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip',
|
||||
{
|
||||
defaultMessage: 'Model has started deployments',
|
||||
}
|
||||
)
|
||||
: null
|
||||
}
|
||||
>
|
||||
<>
|
||||
|
@ -428,7 +436,7 @@ export function useModelActions({
|
|||
enabled: (item) => {
|
||||
// TODO check for permissions to delete ingest pipelines.
|
||||
// ATM undefined means pipelines fetch failed server-side.
|
||||
return !isPopulatedObject(item.pipelines);
|
||||
return item.state !== MODEL_STATE.STARTED && !isPopulatedObject(item.pipelines);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@ import {
|
|||
ELASTIC_MODEL_TAG,
|
||||
ELASTIC_MODEL_TYPE,
|
||||
MODEL_STATE,
|
||||
ModelState,
|
||||
} from '@kbn/ml-trained-models-utils/src/constants/trained_models';
|
||||
import { TechnicalPreviewBadge } from '../components/technical_preview_badge';
|
||||
import { useModelActions } from './model_actions';
|
||||
|
@ -70,7 +71,7 @@ export type ModelItem = TrainedModelConfigResponse & {
|
|||
pipelines?: ModelPipelines['pipelines'] | null;
|
||||
deployment_ids: string[];
|
||||
putModelConfig?: object;
|
||||
state: string;
|
||||
state: ModelState;
|
||||
};
|
||||
|
||||
export type ModelItemFull = Required<ModelItem>;
|
||||
|
|
|
@ -210,10 +210,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
numOfAllocations: 1,
|
||||
threadsPerAllocation: 2,
|
||||
});
|
||||
await ml.trainedModelsTable.assertModelDeleteActionButtonEnabled(model.id, false);
|
||||
});
|
||||
|
||||
it(`stops deployment of the imported model ${model.id}`, async () => {
|
||||
await ml.trainedModelsTable.stopDeployment(model.id);
|
||||
await ml.trainedModelsTable.assertModelDeleteActionButtonEnabled(model.id, true);
|
||||
});
|
||||
|
||||
it(`deletes the imported model ${model.id}`, async () => {
|
||||
|
|
|
@ -29,6 +29,8 @@ export function TrainedModelsTableProvider(
|
|||
) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const find = getService('find');
|
||||
const browser = getService('browser');
|
||||
|
||||
return new (class ModelsTable {
|
||||
public async parseModelsTable() {
|
||||
|
@ -161,9 +163,7 @@ export function TrainedModelsTableProvider(
|
|||
}
|
||||
|
||||
public async assertModelCollapsedActionsButtonExists(modelId: string, expectedValue: boolean) {
|
||||
const actionsExists = await testSubjects.exists(
|
||||
this.rowSelector(modelId, 'euiCollapsedItemActionsButton')
|
||||
);
|
||||
const actionsExists = await this.doesModelCollapsedActionsButtonExist(modelId);
|
||||
expect(actionsExists).to.eql(
|
||||
expectedValue,
|
||||
`Expected row collapsed actions menu button for trained model '${modelId}' to be ${
|
||||
|
@ -172,6 +172,20 @@ export function TrainedModelsTableProvider(
|
|||
);
|
||||
}
|
||||
|
||||
public async doesModelCollapsedActionsButtonExist(modelId: string): Promise<boolean> {
|
||||
return await testSubjects.exists(this.rowSelector(modelId, 'euiCollapsedItemActionsButton'));
|
||||
}
|
||||
|
||||
public async toggleActionsContextMenu(modelId: string, expectOpen = true) {
|
||||
await testSubjects.click(this.rowSelector(modelId, 'euiCollapsedItemActionsButton'));
|
||||
const panelElement = await find.byCssSelector('.euiContextMenuPanel');
|
||||
const isDisplayed = await panelElement.isDisplayed();
|
||||
expect(isDisplayed).to.eql(
|
||||
expectOpen,
|
||||
`Expected the action context menu for '${modelId}' to be ${expectOpen ? 'open' : 'closed'}`
|
||||
);
|
||||
}
|
||||
|
||||
public async assertModelDeleteActionButtonExists(modelId: string, expectedValue: boolean) {
|
||||
const actionsExists = await testSubjects.exists(
|
||||
this.rowSelector(modelId, 'mlModelsTableRowDeleteAction')
|
||||
|
@ -224,10 +238,24 @@ export function TrainedModelsTableProvider(
|
|||
}
|
||||
|
||||
public async assertModelDeleteActionButtonEnabled(modelId: string, expectedValue: boolean) {
|
||||
await this.assertModelDeleteActionButtonExists(modelId, true);
|
||||
const isEnabled = await testSubjects.isEnabled(
|
||||
this.rowSelector(modelId, 'mlModelsTableRowDeleteAction')
|
||||
);
|
||||
const actionsButtonExists = await this.doesModelCollapsedActionsButtonExist(modelId);
|
||||
|
||||
let isEnabled = null;
|
||||
|
||||
if (actionsButtonExists) {
|
||||
await this.toggleActionsContextMenu(modelId, true);
|
||||
const panelElement = await find.byCssSelector('.euiContextMenuPanel');
|
||||
const actionButton = await panelElement.findByTestSubject('mlModelsTableRowDeleteAction');
|
||||
isEnabled = await actionButton.isEnabled();
|
||||
// escape popover
|
||||
await browser.pressKeys(browser.keys.ESCAPE);
|
||||
} else {
|
||||
await this.assertModelDeleteActionButtonExists(modelId, true);
|
||||
isEnabled = await testSubjects.isEnabled(
|
||||
this.rowSelector(modelId, 'mlModelsTableRowDeleteAction')
|
||||
);
|
||||
}
|
||||
|
||||
expect(isEnabled).to.eql(
|
||||
expectedValue,
|
||||
`Expected row delete action button for trained model '${modelId}' to be '${
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue