feat: 卸载应用增加任务日志 (#6043)

This commit is contained in:
zhengkunwang 2024-08-06 16:28:30 +08:00 committed by GitHub
parent d005e7b4c9
commit f2a2c7996f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 201 additions and 127 deletions

View file

@ -1,7 +1,9 @@
package dto
import (
"context"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/task"
)
type AppDatabase struct {
@ -147,3 +149,10 @@ type AppInstallInfo struct {
Key string `json:"key"`
Name string `json:"name"`
}
type DelAppLink struct {
Ctx context.Context
Task *task.Task
Install *model.AppInstall
ForceDelete bool
}

View file

@ -88,10 +88,11 @@ type AppInstallUpgrade struct {
type AppInstallDelete struct {
Install model.AppInstall
DeleteBackup bool `json:"deleteBackup"`
ForceDelete bool `json:"forceDelete"`
DeleteDB bool `json:"deleteDB"`
DeleteImage bool `json:"deleteImage"`
DeleteBackup bool `json:"deleteBackup"`
ForceDelete bool `json:"forceDelete"`
DeleteDB bool `json:"deleteDB"`
DeleteImage bool `json:"deleteImage"`
TaskID string `json:"taskID"`
}
type AppInstalledUpdate struct {

View file

@ -265,6 +265,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
ForceDelete: req.ForceDelete,
DeleteDB: req.DeleteDB,
DeleteImage: req.DeleteImage,
TaskID: req.TaskID,
}
if err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete {
return err

View file

@ -134,7 +134,12 @@ var ToolKeys = map[string]uint{
func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
deleteAppLink := func(t *task.Task) {
_ = deleteLink(ctx, appInstall, true, true, true)
del := dto.DelAppLink{
Ctx: ctx,
Install: appInstall,
ForceDelete: true,
}
_ = deleteLink(del)
}
var dbConfig dto.AppDatabase
if DatabaseKeys[app.Key] > 0 {
@ -315,138 +320,171 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
}
func deleteAppInstall(deleteReq request.AppInstallDelete) error {
op := files.NewFileOp()
install := deleteReq.Install
op := files.NewFileOp()
appDir := install.GetPath()
dir, _ := os.Stat(appDir)
if dir != nil {
out, err := compose.Down(install.GetComposePath())
if err != nil && !deleteReq.ForceDelete {
return handleErr(install, err, out)
}
//TODO use task
if err = runScript(nil, &install, "uninstall"); err != nil {
_, _ = compose.Up(install.GetComposePath())
return err
}
if deleteReq.DeleteImage {
images, _ := getImages(install)
client, err := docker.NewClient()
if err != nil {
uninstallTask, err := task.NewTaskWithOps(install.Name, task.TaskUninstall, task.TaskScopeApp, deleteReq.TaskID, install.ID)
if err != nil {
return err
}
uninstall := func(t *task.Task) error {
dir, _ := os.Stat(appDir)
if dir != nil {
logStr := i18n.GetMsgByKey("Stop") + i18n.GetMsgByKey("App")
t.Log(logStr)
out, err := compose.Down(install.GetComposePath())
if err != nil && !deleteReq.ForceDelete {
return handleErr(install, err, out)
}
t.LogSuccess(logStr)
if err = runScript(t, &install, "uninstall"); err != nil {
_, _ = compose.Up(install.GetComposePath())
return err
}
defer client.Close()
for _, image := range images {
imageID, err := client.GetImageIDByName(image)
if err == nil {
if err = client.DeleteImage(imageID); err != nil {
global.LOG.Errorf("delete image %s error %s", image, err.Error())
if deleteReq.DeleteImage {
delImageStr := i18n.GetMsgByKey("TaskDelete") + i18n.GetMsgByKey("Image")
images, _ := getImages(install)
client, err := docker.NewClient()
if err != nil {
return err
}
defer client.Close()
for _, image := range images {
imageID, err := client.GetImageIDByName(image)
if err == nil {
imgStr := delImageStr + image
t.Log(imgStr)
if err = client.DeleteImage(imageID); err != nil {
t.LogFailedWithErr(imgStr, err)
continue
}
t.LogSuccess(delImageStr + image)
}
}
}
}
}
tx, ctx := helper.GetTxAndContext()
defer tx.Rollback()
if err := appInstallRepo.Delete(ctx, install); err != nil {
return err
}
if err := deleteLink(ctx, &install, deleteReq.DeleteDB, deleteReq.ForceDelete, deleteReq.DeleteBackup); err != nil && !deleteReq.ForceDelete {
return err
}
tx, ctx := helper.GetTxAndContext()
defer tx.Rollback()
if err = appInstallRepo.Delete(ctx, install); err != nil {
return err
}
if DatabaseKeys[install.App.Key] > 0 {
_ = databaseRepo.Delete(ctx, databaseRepo.WithAppInstallID(install.ID))
}
if deleteReq.DeleteDB {
del := dto.DelAppLink{
Ctx: ctx,
Install: &install,
ForceDelete: deleteReq.ForceDelete,
Task: uninstallTask,
}
t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
if err = deleteLink(del); err != nil {
t.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"), err)
if !deleteReq.ForceDelete {
return err
}
}
t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
}
switch install.App.Key {
case constant.AppOpenresty:
websites, _ := websiteRepo.List()
for _, website := range websites {
if website.AppInstallID > 0 {
websiteAppInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if websiteAppInstall.AppId > 0 {
websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId))
if websiteApp.Type == constant.RuntimePHP {
go func() {
_, _ = compose.Down(websiteAppInstall.GetComposePath())
_ = op.DeleteDir(websiteAppInstall.GetPath())
}()
_ = appInstallRepo.Delete(ctx, websiteAppInstall)
if DatabaseKeys[install.App.Key] > 0 {
_ = databaseRepo.Delete(ctx, databaseRepo.WithAppInstallID(install.ID))
}
switch install.App.Key {
case constant.AppOpenresty:
websites, _ := websiteRepo.List()
for _, website := range websites {
if website.AppInstallID > 0 {
websiteAppInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if websiteAppInstall.AppId > 0 {
websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId))
if websiteApp.Type == constant.RuntimePHP {
go func() {
_, _ = compose.Down(websiteAppInstall.GetComposePath())
_ = op.DeleteDir(websiteAppInstall.GetPath())
}()
_ = appInstallRepo.Delete(ctx, websiteAppInstall)
}
}
}
}
_ = websiteRepo.DeleteAll(ctx)
_ = websiteDomainRepo.DeleteAll(ctx)
xpack.RemoveTamper("")
case constant.AppMysql, constant.AppMariaDB:
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
case constant.AppPostgresql:
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
}
_ = websiteRepo.DeleteAll(ctx)
_ = websiteDomainRepo.DeleteAll(ctx)
xpack.RemoveTamper("")
case constant.AppMysql, constant.AppMariaDB:
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
case constant.AppPostgresql:
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/app/%s/%s", install.App.Key, install.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
if deleteReq.DeleteBackup {
localDir, _ := loadLocalDir()
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", install.App.Key, install.Name))
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/app/%s/%s", install.App.Key, install.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
global.LOG.Infof("delete app %s-%s backups successful", install.App.Key, install.Name)
if deleteReq.DeleteBackup {
localDir, _ := loadLocalDir()
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", install.App.Key, install.Name))
if _, err = os.Stat(backupDir); err == nil {
t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("TaskBackup"))
_ = os.RemoveAll(backupDir)
t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("TaskBackup"))
}
}
_ = op.DeleteDir(appDir)
tx.Commit()
return nil
}
_ = op.DeleteDir(appDir)
tx.Commit()
uninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil)
go uninstallTask.Execute()
return nil
}
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
func deleteLink(del dto.DelAppLink) error {
install := del.Install
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
if len(resources) == 0 {
return nil
}
for _, re := range resources {
if deleteDB {
switch re.Key {
case constant.AppMysql, constant.AppMariaDB:
mysqlService := NewIMysqlService()
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !forceDelete {
return err
}
case constant.AppPostgresql:
pgsqlService := NewIPostgresqlService()
database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabasePostgresql{}) {
continue
}
if err := pgsqlService.Delete(ctx, dto.PostgresqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.PostgresqlName,
}); err != nil {
return err
}
switch re.Key {
case constant.AppMysql, constant.AppMariaDB:
mysqlService := NewIMysqlService()
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(del.Ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: del.ForceDelete,
DeleteBackup: true,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !del.ForceDelete {
return err
}
case constant.AppPostgresql:
pgsqlService := NewIPostgresqlService()
database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabasePostgresql{}) {
continue
}
if err := pgsqlService.Delete(del.Ctx, dto.PostgresqlDBDelete{
ID: database.ID,
ForceDelete: del.ForceDelete,
DeleteBackup: true,
Type: re.Key,
Database: database.PostgresqlName,
}); err != nil {
return err
}
}
}
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
return appInstallResourceRepo.DeleteBy(del.Ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {

View file

@ -219,3 +219,15 @@ func (t *Task) LogSuccess(msg string) {
func (t *Task) LogStart(msg string) {
t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg))
}
func (t *Task) LogWithOps(operate, msg string) {
t.Logger.Printf("%s%s", i18n.GetMsgByKey(operate), msg)
}
func (t *Task) LogSuccessWithOps(operate, msg string) {
t.Logger.Printf("%s%s%s", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Success"))
}
func (t *Task) LogFailedWithOps(operate, msg string, err error) {
t.Logger.Printf("%s%s%s : %s ", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Failed"), err.Error())
}

View file

@ -230,4 +230,7 @@ ExecShell: "Execute {{ .name }} Script"
PullImage: "Pull Image"
Start: "Start"
Run: "Run"
Stop: 'Stop',
Image: 'Image',
AppLink: 'Associated Application'

View file

@ -232,3 +232,7 @@ ExecShell: "執行 {{ .name }} 腳本"
PullImage: "拉取鏡像"
Start: "開始"
Run: "啟動"
Stop: '停止',
Image: '鏡像',
AppLink: '關聯應用'

View file

@ -234,3 +234,6 @@ ExecShell: "执行 {{ .name }} 脚本"
PullImage: "拉取镜像"
Start: "开始"
Run: "启动"
Stop: "停止"
Image: "镜像"
AppLink: "关联应用"

View file

@ -189,6 +189,7 @@ export namespace App {
forceDelete?: boolean;
deleteBackup?: boolean;
deleteImage?: boolean;
taskID?: string;
}
export interface AppInstalledSearch extends ReqPage {

View file

@ -45,6 +45,7 @@ const scrollerElement = ref<HTMLElement | null>(null);
const minPage = ref(1);
const maxPage = ref(1);
const open = ref(false);
const em = defineEmits(['close']);
const readReq = reactive({
taskID: '',
@ -148,6 +149,7 @@ const changeTail = (fromOutSide: boolean) => {
const handleClose = () => {
onCloseLog();
open.value = false;
em('close', open.value);
};
const onCloseLog = async () => {

View file

@ -47,6 +47,7 @@
</span>
</template>
</el-dialog>
<TaskLog ref="taskLogRef" @close="handleClose" />
</template>
<script lang="ts" setup>
import { FormInstance } from 'element-plus';
@ -54,23 +55,26 @@ import { onBeforeUnmount, ref } from 'vue';
import { App } from '@/api/interface/app';
import { InstalledOp } from '@/api/modules/app';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import bus from '../../bus';
import TaskLog from '@/components/task-log/index.vue';
import { v4 as uuidv4 } from 'uuid';
let deleteReq = ref({
const deleteReq = ref({
operate: 'delete',
installId: 0,
deleteBackup: false,
forceDelete: false,
deleteDB: true,
deleteImage: true,
taskID: '',
});
let open = ref(false);
let loading = ref(false);
let deleteHelper = ref('');
let deleteInfo = ref('');
let appInstallName = ref('');
let appType = ref('');
const open = ref(false);
const loading = ref(false);
const deleteHelper = ref('');
const deleteInfo = ref('');
const appInstallName = ref('');
const appType = ref('');
const taskLogRef = ref();
const deleteForm = ref<FormInstance>();
const em = defineEmits(['close']);
@ -88,6 +92,7 @@ const acceptParams = async (app: App.AppInstallDto) => {
forceDelete: false,
deleteDB: true,
deleteImage: true,
taskID: uuidv4(),
};
deleteInfo.value = '';
deleteReq.value.installId = app.id;
@ -98,16 +103,11 @@ const acceptParams = async (app: App.AppInstallDto) => {
};
const submit = async () => {
loading.value = true;
InstalledOp(deleteReq.value)
.then(() => {
handleClose();
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
bus.emit('update', true);
})
.finally(() => {
loading.value = false;
});
InstalledOp(deleteReq.value).then(() => {
handleClose();
taskLogRef.value.openWithTaskID(deleteReq.value.taskID);
bus.emit('update', true);
});
};
onBeforeUnmount(() => {