mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-26 16:56:22 +08:00
feat: 卸载应用支持删除镜像 (#6040)
This commit is contained in:
parent
370463366b
commit
e59072e5a7
10 changed files with 81 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -49,6 +49,7 @@ core/xpack
|
||||||
core/router/entry_xpack.go
|
core/router/entry_xpack.go
|
||||||
core/server/init_xpack.go
|
core/server/init_xpack.go
|
||||||
core/utils/xpack/xpack_xpack.go
|
core/utils/xpack/xpack_xpack.go
|
||||||
|
xpack
|
||||||
|
|
||||||
.history/
|
.history/
|
||||||
dist/
|
dist/
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -73,6 +74,7 @@ type AppInstalledOperate struct {
|
||||||
PullImage bool `json:"pullImage"`
|
PullImage bool `json:"pullImage"`
|
||||||
DockerCompose string `json:"dockerCompose"`
|
DockerCompose string `json:"dockerCompose"`
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
|
DeleteImage bool `json:"deleteImage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppInstallUpgrade struct {
|
type AppInstallUpgrade struct {
|
||||||
|
|
@ -84,6 +86,14 @@ type AppInstallUpgrade struct {
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppInstallDelete struct {
|
||||||
|
Install model.AppInstall
|
||||||
|
DeleteBackup bool `json:"deleteBackup"`
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
DeleteDB bool `json:"deleteDB"`
|
||||||
|
DeleteImage bool `json:"deleteImage"`
|
||||||
|
}
|
||||||
|
|
||||||
type AppInstalledUpdate struct {
|
type AppInstalledUpdate struct {
|
||||||
InstallId uint `json:"installId" validate:"required"`
|
InstallId uint `json:"installId" validate:"required"`
|
||||||
Params map[string]interface{} `json:"params" validate:"required"`
|
Params map[string]interface{} `json:"params" validate:"required"`
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,14 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||||
}
|
}
|
||||||
return syncAppInstallStatus(&install, false)
|
return syncAppInstallStatus(&install, false)
|
||||||
case constant.Delete:
|
case constant.Delete:
|
||||||
if err := deleteAppInstall(install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
|
deleteReq := request.AppInstallDelete{
|
||||||
|
Install: install,
|
||||||
|
DeleteBackup: req.DeleteBackup,
|
||||||
|
ForceDelete: req.ForceDelete,
|
||||||
|
DeleteDB: req.DeleteDB,
|
||||||
|
DeleteImage: req.DeleteImage,
|
||||||
|
}
|
||||||
|
if err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -314,13 +314,14 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete bool, deleteDB bool) error {
|
func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||||
op := files.NewFileOp()
|
op := files.NewFileOp()
|
||||||
|
install := deleteReq.Install
|
||||||
appDir := install.GetPath()
|
appDir := install.GetPath()
|
||||||
dir, _ := os.Stat(appDir)
|
dir, _ := os.Stat(appDir)
|
||||||
if dir != nil {
|
if dir != nil {
|
||||||
out, err := compose.Down(install.GetComposePath())
|
out, err := compose.Down(install.GetComposePath())
|
||||||
if err != nil && !forceDelete {
|
if err != nil && !deleteReq.ForceDelete {
|
||||||
return handleErr(install, err, out)
|
return handleErr(install, err, out)
|
||||||
}
|
}
|
||||||
//TODO use task
|
//TODO use task
|
||||||
|
|
@ -328,13 +329,29 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||||
_, _ = compose.Up(install.GetComposePath())
|
_, _ = compose.Up(install.GetComposePath())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if deleteReq.DeleteImage {
|
||||||
|
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 {
|
||||||
|
if err = client.DeleteImage(imageID); err != nil {
|
||||||
|
global.LOG.Errorf("delete image %s error %s", image, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tx, ctx := helper.GetTxAndContext()
|
tx, ctx := helper.GetTxAndContext()
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := deleteLink(ctx, &install, deleteDB, forceDelete, deleteBackup); err != nil && !forceDelete {
|
if err := deleteLink(ctx, &install, deleteReq.DeleteDB, deleteReq.ForceDelete, deleteReq.DeleteBackup); err != nil && !deleteReq.ForceDelete {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -374,7 +391,7 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||||
if _, err := os.Stat(uploadDir); err == nil {
|
if _, err := os.Stat(uploadDir); err == nil {
|
||||||
_ = os.RemoveAll(uploadDir)
|
_ = os.RemoveAll(uploadDir)
|
||||||
}
|
}
|
||||||
if deleteBackup {
|
if deleteReq.DeleteBackup {
|
||||||
localDir, _ := loadLocalDir()
|
localDir, _ := loadLocalDir()
|
||||||
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", install.App.Key, install.Name))
|
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", install.App.Key, install.Name))
|
||||||
if _, err := os.Stat(backupDir); err == nil {
|
if _, err := os.Stat(backupDir); err == nil {
|
||||||
|
|
@ -691,6 +708,22 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||||
return containerNames, nil
|
return containerNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getImages(install model.AppInstall) ([]string, error) {
|
||||||
|
envStr, err := coverEnvJsonToStr(install.Env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var images []string
|
||||||
|
for _, service := range project.AllServices() {
|
||||||
|
images = append(images, service.Image)
|
||||||
|
}
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
|
||||||
func coverEnvJsonToStr(envJson string) (string, error) {
|
func coverEnvJsonToStr(envJson string) (string, error) {
|
||||||
envMap := make(map[string]interface{})
|
envMap := make(map[string]interface{})
|
||||||
_ = json.Unmarshal([]byte(envJson), &envMap)
|
_ = json.Unmarshal([]byte(envJson), &envMap)
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,13 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
|
||||||
if checkIsLinkApp(website) && req.DeleteApp {
|
if checkIsLinkApp(website) && req.DeleteApp {
|
||||||
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||||
if appInstall.ID > 0 {
|
if appInstall.ID > 0 {
|
||||||
if err = deleteAppInstall(appInstall, true, req.ForceDelete, true); err != nil && !req.ForceDelete {
|
deleteReq := request.AppInstallDelete{
|
||||||
|
Install: appInstall,
|
||||||
|
ForceDelete: req.ForceDelete,
|
||||||
|
DeleteBackup: true,
|
||||||
|
DeleteDB: true,
|
||||||
|
}
|
||||||
|
if err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ export namespace App {
|
||||||
detailId?: number;
|
detailId?: number;
|
||||||
forceDelete?: boolean;
|
forceDelete?: boolean;
|
||||||
deleteBackup?: boolean;
|
deleteBackup?: boolean;
|
||||||
|
deleteImage?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstalledSearch extends ReqPage {
|
export interface AppInstalledSearch extends ReqPage {
|
||||||
|
|
|
||||||
|
|
@ -1856,6 +1856,10 @@ const message = {
|
||||||
useCustom: 'Customize docker-compose.yml',
|
useCustom: 'Customize docker-compose.yml',
|
||||||
useCustomHelper:
|
useCustomHelper:
|
||||||
'Using a custom docker-compose.yml file may cause the application upgrade to fail. If it is not necessary, do not check it',
|
'Using a custom docker-compose.yml file may cause the application upgrade to fail. If it is not necessary, do not check it',
|
||||||
|
diffHelper:
|
||||||
|
'The left side is the old version, the right side is the new version. After editing, click to save the custom version',
|
||||||
|
deleteImage: 'Delete Image',
|
||||||
|
deleteImageHelper: 'Delete the image related to the application. The task will not stop if deletion fails',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: 'Website',
|
website: 'Website',
|
||||||
|
|
|
||||||
|
|
@ -1723,7 +1723,9 @@ const message = {
|
||||||
useDefault: '使用預設版本',
|
useDefault: '使用預設版本',
|
||||||
useCustom: '自訂 docker-compose.yml',
|
useCustom: '自訂 docker-compose.yml',
|
||||||
useCustomHelper: '使用自訂 docker-compose.yml 文件,可能會導致應用程式升級失敗,如無必要,請勿勾選',
|
useCustomHelper: '使用自訂 docker-compose.yml 文件,可能會導致應用程式升級失敗,如無必要,請勿勾選',
|
||||||
diffHelper: '左側為舊版本,右側為新版,編輯之後點選使用自訂版本儲存',
|
diffHelper: '左側為舊版本,右側為新版,編輯之後點擊使用自訂版本保存',
|
||||||
|
deleteImage: '刪除鏡像',
|
||||||
|
deleteImageHelper: '刪除應用相關鏡像,刪除失敗任務不會終止',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '網站',
|
website: '網站',
|
||||||
|
|
|
||||||
|
|
@ -1725,6 +1725,8 @@ const message = {
|
||||||
useCustom: '自定义 docker-compose.yml',
|
useCustom: '自定义 docker-compose.yml',
|
||||||
useCustomHelper: '使用自定义 docker-compose.yml 文件,可能会导致应用升级失败,如无必要,请勿勾选',
|
useCustomHelper: '使用自定义 docker-compose.yml 文件,可能会导致应用升级失败,如无必要,请勿勾选',
|
||||||
diffHelper: '左侧为旧版本,右侧为新版,编辑之后点击使用自定义版本保存',
|
diffHelper: '左侧为旧版本,右侧为新版,编辑之后点击使用自定义版本保存',
|
||||||
|
deleteImage: '删除镜像',
|
||||||
|
deleteImageHelper: '删除应用相关镜像,删除失败任务不会终止',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '网站',
|
website: '网站',
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,12 @@
|
||||||
{{ $t('app.deleteBackupHelper') }}
|
{{ $t('app.deleteBackupHelper') }}
|
||||||
</span>
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteReq.deleteImage" :label="$t('app.deleteImage')" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('app.deleteImageHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item v-if="appType === 'website'">
|
<el-form-item v-if="appType === 'website'">
|
||||||
<el-checkbox v-model="deleteReq.deleteDB" :label="$t('app.deleteDB')" />
|
<el-checkbox v-model="deleteReq.deleteDB" :label="$t('app.deleteDB')" />
|
||||||
<span class="input-help">
|
<span class="input-help">
|
||||||
|
|
@ -57,6 +63,7 @@ let deleteReq = ref({
|
||||||
deleteBackup: false,
|
deleteBackup: false,
|
||||||
forceDelete: false,
|
forceDelete: false,
|
||||||
deleteDB: true,
|
deleteDB: true,
|
||||||
|
deleteImage: true,
|
||||||
});
|
});
|
||||||
let open = ref(false);
|
let open = ref(false);
|
||||||
let loading = ref(false);
|
let loading = ref(false);
|
||||||
|
|
@ -80,6 +87,7 @@ const acceptParams = async (app: App.AppInstallDto) => {
|
||||||
deleteBackup: false,
|
deleteBackup: false,
|
||||||
forceDelete: false,
|
forceDelete: false,
|
||||||
deleteDB: true,
|
deleteDB: true,
|
||||||
|
deleteImage: true,
|
||||||
};
|
};
|
||||||
deleteInfo.value = '';
|
deleteInfo.value = '';
|
||||||
deleteReq.value.installId = app.id;
|
deleteReq.value.installId = app.id;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue