mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-30 08:05:43 +00:00
fix(ai): allow stopping and deleting restarting model containers (#14250)
This commit is contained in:
@@ -259,6 +259,15 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if install.Status == constant.StatusRestarting && (req.Operate == constant.Stop || req.Operate == constant.Delete) && !req.ForceDelete {
|
||||
count, err := taskRepo.CountExecutingTask(repo.WithByType(task.TaskScopeApp), taskRepo.WithResourceID(install.ID), taskRepo.WithOperate(task.TaskRestart))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return buserr.New("TaskIsExecuting")
|
||||
}
|
||||
}
|
||||
if !req.ForceDelete && !files.NewFileOp().Stat(install.GetPath()) {
|
||||
return buserr.New("ErrInstallDirNotFound")
|
||||
}
|
||||
@@ -342,9 +351,6 @@ func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppIns
|
||||
return errors.New("lifecycle script operation not supported")
|
||||
}
|
||||
install.Message = ""
|
||||
if err := appInstallRepo.Save(context.Background(), &install); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
operationTask, err := task.NewTaskWithOps(install.Name, taskType, task.TaskScopeApp, req.TaskID, install.ID)
|
||||
if err != nil {
|
||||
@@ -355,10 +361,17 @@ func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppIns
|
||||
}
|
||||
return err
|
||||
}
|
||||
ready := make(chan error, 1)
|
||||
started := make(chan struct{})
|
||||
operation := string(req.Operate)
|
||||
operationTask.AddSubTaskWithOps(
|
||||
task.GetTaskName(install.Name, taskType, task.TaskScopeApp),
|
||||
func(t *task.Task) error {
|
||||
if err := appInstallRepo.Save(context.Background(), &install); err != nil {
|
||||
return err
|
||||
}
|
||||
close(started)
|
||||
ready <- nil
|
||||
if err := runScript(t, &install, operation); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -385,6 +398,12 @@ func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppIns
|
||||
)
|
||||
go func() {
|
||||
if taskErr := operationTask.Execute(); taskErr != nil {
|
||||
select {
|
||||
case <-started:
|
||||
default:
|
||||
ready <- taskErr
|
||||
return
|
||||
}
|
||||
if onFailure != nil {
|
||||
onFailure(taskErr)
|
||||
return
|
||||
@@ -394,7 +413,7 @@ func operateAppWithLifecycleScripts(install model.AppInstall, req request.AppIns
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
return <-ready
|
||||
}
|
||||
|
||||
func (a *AppInstallService) UpdateAppConfig(req request.AppConfigUpdate) error {
|
||||
@@ -1036,8 +1055,16 @@ func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
|
||||
}
|
||||
switch appInstall.Status {
|
||||
case constant.StatusInstalling, constant.StatusRebuilding, constant.StatusUpgrading, constant.StatusUninstalling,
|
||||
constant.StatusStarting, constant.StatusRestarting, constant.StatusWaiting:
|
||||
constant.StatusStarting, constant.StatusWaiting:
|
||||
return nil
|
||||
case constant.StatusRestarting:
|
||||
count, err := taskRepo.CountExecutingTask(repo.WithByType(task.TaskScopeApp), taskRepo.WithResourceID(appInstall.ID), taskRepo.WithOperate(task.TaskRestart))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
case constant.StatusInstallErr, constant.StatusUpgradeErr, constant.StatusUpErr:
|
||||
if !force {
|
||||
return nil
|
||||
|
||||
@@ -316,6 +316,8 @@ func (t *Task) ExecuteQueued(slots chan struct{}, waitingStep string, onFinish f
|
||||
|
||||
func (t *Task) Execute() error {
|
||||
if err := t.taskRepo.Save(context.Background(), t.Task); err != nil {
|
||||
_ = t.logFile.Close()
|
||||
global.RemoveTaskCancel(t.TaskID)
|
||||
return err
|
||||
}
|
||||
return t.executeRegistered()
|
||||
|
||||
@@ -357,7 +357,9 @@ const deploymentButtons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
permission: true,
|
||||
disabled: (row: Deployment) => operationDisabled(row) && row.status !== 'Uninstalling',
|
||||
disabled: (row: Deployment) =>
|
||||
deleteLoading.value ||
|
||||
(operationDisabled(row) && !['Uninstalling', 'ReStarting', 'Restarting'].includes(row.status)),
|
||||
click: (row: Deployment) => {
|
||||
deleting.value = row;
|
||||
forceDelete.value = false;
|
||||
|
||||
@@ -230,8 +230,13 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-dropdown placement="bottom" :disabled="statusDisabled(row)">
|
||||
<Status v-permission :status="row.status" operate :disabled="statusDisabled(row)" />
|
||||
<el-dropdown placement="bottom" :disabled="statusDisabled(row, 'stop')">
|
||||
<Status
|
||||
v-permission
|
||||
:status="row.status"
|
||||
operate
|
||||
:disabled="statusDisabled(row, 'stop')"
|
||||
/>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@@ -242,7 +247,8 @@
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:disabled="
|
||||
statusDisabled(row) || ['Stopped', 'Exited'].includes(row.status)
|
||||
statusDisabled(row, 'stop') ||
|
||||
['Stopped', 'Exited'].includes(row.status)
|
||||
"
|
||||
@click="operate(row, 'stop')"
|
||||
>
|
||||
@@ -569,22 +575,23 @@ const accountButtons = [
|
||||
];
|
||||
const taskRef = ref();
|
||||
const pending = ref('');
|
||||
const operationDisabled = (row: Deployment) =>
|
||||
const operationDisabled = (row: Deployment, operation = '') =>
|
||||
Boolean(pending.value) ||
|
||||
['Installing', 'Starting', 'ReStarting', 'Restarting', 'Rebuilding', 'Waiting', 'Uninstalling'].includes(
|
||||
row.status,
|
||||
);
|
||||
const statusDisabled = (row: Deployment) =>
|
||||
operationDisabled(row) || !hasManagePermissionAccess() || (row.engine === 'ollama' && !hasPermission('app_manage'));
|
||||
['Installing', 'Starting', 'Rebuilding', 'Waiting', 'Uninstalling'].includes(row.status) ||
|
||||
(['ReStarting', 'Restarting'].includes(row.status) && !['stop', 'delete'].includes(operation));
|
||||
const statusDisabled = (row: Deployment, operation = '') =>
|
||||
operationDisabled(row, operation) ||
|
||||
!hasManagePermissionAccess() ||
|
||||
(row.engine === 'ollama' && !hasPermission('app_manage'));
|
||||
const operate = async (row: Deployment, operation: 'start' | 'stop' | 'restart') => {
|
||||
if (statusDisabled(row)) return;
|
||||
if (statusDisabled(row, operation)) return;
|
||||
const node = currentNode.value;
|
||||
await ElMessageBox.confirm(
|
||||
i18n.global.t('app.operatorHelper', [i18n.global.t('commons.operate.' + operation)]),
|
||||
i18n.global.t('commons.operate.' + operation),
|
||||
{ type: 'warning' },
|
||||
);
|
||||
if (node !== currentNode.value || statusDisabled(row)) return;
|
||||
if (node !== currentNode.value || statusDisabled(row, operation)) return;
|
||||
pending.value = `${row.engine}:${row.id}`;
|
||||
try {
|
||||
if (row.engine === 'ollama') {
|
||||
@@ -652,7 +659,7 @@ const deploymentButtons = [
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
permission: true,
|
||||
show: (row: Deployment) => row.engine === 'ollama' && hasPermission('app_manage'),
|
||||
disabled: operationDisabled,
|
||||
disabled: (row: Deployment) => operationDisabled(row, 'delete'),
|
||||
click: async (row: Deployment) => {
|
||||
const node = currentNode.value;
|
||||
const path = route.fullPath;
|
||||
@@ -682,7 +689,7 @@ const deploymentButtons = [
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
permission: true,
|
||||
show: (row: Deployment) => row.engine === 'vllm',
|
||||
disabled: (row: Deployment) => operationDisabled(row) && row.status !== 'Uninstalling',
|
||||
disabled: (row: Deployment) => operationDisabled(row, 'delete') && row.status !== 'Uninstalling',
|
||||
click: (row: Deployment) => manageRef.value.open(row, 'delete', true),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -211,7 +211,7 @@ const buttons = [
|
||||
label: i18n.global.t('commons.button.stop'),
|
||||
permission: true,
|
||||
disabled: (row: AI.TensorRTLLM) => {
|
||||
return row.status !== 'Running';
|
||||
return !['Running', 'ReStarting', 'Restarting'].includes(row.status);
|
||||
},
|
||||
click: (row: AI.TensorRTLLM) => {
|
||||
operate(row, 'stop');
|
||||
|
||||
Reference in New Issue
Block a user