mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-10 23:47:39 +08:00
fix: Optimize container cleanup mechanism (#9795)
Refs #7444 涉及到容器、镜像、网络、存储卷、构建缓存清理
This commit is contained in:
parent
ba4307c1dc
commit
9c292bebf0
25 changed files with 280 additions and 169 deletions
|
@ -315,7 +315,7 @@ func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
|
||||||
// @Summary Clean container
|
// @Summary Clean container
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.ContainerPrune true "request"
|
// @Param request body dto.ContainerPrune true "request"
|
||||||
// @Success 200 {object} dto.ContainerPruneReport
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /containers/prune [post]
|
// @Router /containers/prune [post]
|
||||||
|
@ -326,12 +326,11 @@ func (b *BaseApi) ContainerPrune(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
report, err := containerService.Prune(req)
|
if err := containerService.Prune(req); err != nil {
|
||||||
if err != nil {
|
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
helper.SuccessWithData(c, report)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Container
|
// @Tags Container
|
||||||
|
|
|
@ -138,7 +138,7 @@ func (b *BaseApi) ImagePush(c *gin.Context) {
|
||||||
// @Summary Delete image
|
// @Summary Delete image
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.BatchDelete true "request"
|
// @Param request body dto.BatchDelete true "request"
|
||||||
// @Success 200 {object} dto.ContainerPruneReport
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /containers/image/remove [post]
|
// @Router /containers/image/remove [post]
|
||||||
|
@ -149,13 +149,12 @@ func (b *BaseApi) ImageRemove(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := imageService.ImageRemove(req)
|
if err := imageService.ImageRemove(req); err != nil {
|
||||||
if err != nil {
|
|
||||||
helper.InternalServer(c, err)
|
helper.InternalServer(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.SuccessWithData(c, data)
|
helper.Success(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags Container Image
|
// @Tags Container Image
|
||||||
|
|
|
@ -174,15 +174,11 @@ type ContainerCommit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerPrune struct {
|
type ContainerPrune struct {
|
||||||
|
TaskID string `json:"taskID"`
|
||||||
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
|
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
|
||||||
WithTagAll bool `json:"withTagAll"`
|
WithTagAll bool `json:"withTagAll"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerPruneReport struct {
|
|
||||||
DeletedNumber int `json:"deletedNumber"`
|
|
||||||
SpaceReclaimed int `json:"spaceReclaimed"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Network struct {
|
type Network struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -227,8 +223,9 @@ type VolumeCreate struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchDelete struct {
|
type BatchDelete struct {
|
||||||
Force bool `json:"force"`
|
TaskID string `json:"taskID"`
|
||||||
Names []string `json:"names" validate:"required"`
|
Force bool `json:"force"`
|
||||||
|
Names []string `json:"names" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ComposeInfo struct {
|
type ComposeInfo struct {
|
||||||
|
|
|
@ -82,7 +82,7 @@ type IContainerService interface {
|
||||||
CreateVolume(req dto.VolumeCreate) error
|
CreateVolume(req dto.VolumeCreate) error
|
||||||
TestCompose(req dto.ComposeCreate) (bool, error)
|
TestCompose(req dto.ComposeCreate) (bool, error)
|
||||||
ComposeUpdate(req dto.ComposeUpdate) error
|
ComposeUpdate(req dto.ComposeUpdate) error
|
||||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
Prune(req dto.ContainerPrune) error
|
||||||
|
|
||||||
LoadUsers(req dto.OperationWithName) []string
|
LoadUsers(req dto.OperationWithName) []string
|
||||||
|
|
||||||
|
@ -417,66 +417,93 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||||
return string(bytes), nil
|
return string(bytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) {
|
func (u *ContainerService) Prune(req dto.ContainerPrune) error {
|
||||||
report := dto.ContainerPruneReport{}
|
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return report, err
|
return err
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
pruneFilters := filters.NewArgs()
|
name := ""
|
||||||
if req.WithTagAll {
|
|
||||||
pruneFilters.Add("dangling", "false")
|
|
||||||
if req.PruneType != "image" {
|
|
||||||
pruneFilters.Add("until", "24h")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch req.PruneType {
|
switch req.PruneType {
|
||||||
case "container":
|
case "container":
|
||||||
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
|
name = "Container"
|
||||||
if err != nil {
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
report.DeletedNumber = len(rep.ContainersDeleted)
|
|
||||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
|
||||||
case "image":
|
case "image":
|
||||||
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
|
name = "Image"
|
||||||
if err != nil {
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
report.DeletedNumber = len(rep.ImagesDeleted)
|
|
||||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
|
||||||
case "network":
|
|
||||||
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
|
|
||||||
if err != nil {
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
report.DeletedNumber = len(rep.NetworksDeleted)
|
|
||||||
case "volume":
|
case "volume":
|
||||||
versions, err := client.ServerVersion(context.Background())
|
name = "Volume"
|
||||||
if err != nil {
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
|
||||||
pruneFilters.Add("all", "true")
|
|
||||||
}
|
|
||||||
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
|
||||||
if err != nil {
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
report.DeletedNumber = len(rep.VolumesDeleted)
|
|
||||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
|
||||||
case "buildcache":
|
case "buildcache":
|
||||||
opts := build.CachePruneOptions{}
|
name = "BuildCache"
|
||||||
opts.All = true
|
case "network":
|
||||||
rep, err := client.BuildCachePrune(context.Background(), opts)
|
name = "Network"
|
||||||
if err != nil {
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
report.DeletedNumber = len(rep.CachesDeleted)
|
|
||||||
report.SpaceReclaimed = int(rep.SpaceReclaimed)
|
|
||||||
}
|
}
|
||||||
return report, nil
|
taskItem, err := task.NewTaskWithOps(i18n.GetMsgByKey(name), task.TaskClean, task.TaskScopeContainer, req.TaskID, 1)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
taskItem.AddSubTask(i18n.GetMsgByKey("TaskClean"), func(t *task.Task) error {
|
||||||
|
pruneFilters := filters.NewArgs()
|
||||||
|
if req.WithTagAll {
|
||||||
|
pruneFilters.Add("dangling", "false")
|
||||||
|
if req.PruneType != "image" {
|
||||||
|
pruneFilters.Add("until", "24h")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeletedNumber := 0
|
||||||
|
SpaceReclaimed := 0
|
||||||
|
switch req.PruneType {
|
||||||
|
case "container":
|
||||||
|
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
DeletedNumber = len(rep.ContainersDeleted)
|
||||||
|
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||||
|
case "image":
|
||||||
|
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
DeletedNumber = len(rep.ImagesDeleted)
|
||||||
|
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||||
|
case "network":
|
||||||
|
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
DeletedNumber = len(rep.NetworksDeleted)
|
||||||
|
case "volume":
|
||||||
|
versions, err := client.ServerVersion(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
|
||||||
|
pruneFilters.Add("all", "true")
|
||||||
|
}
|
||||||
|
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
DeletedNumber = len(rep.VolumesDeleted)
|
||||||
|
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||||
|
case "buildcache":
|
||||||
|
opts := build.CachePruneOptions{}
|
||||||
|
opts.All = true
|
||||||
|
rep, err := client.BuildCachePrune(context.Background(), opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
DeletedNumber = len(rep.CachesDeleted)
|
||||||
|
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||||
|
}
|
||||||
|
taskItem.Log(i18n.GetMsgWithMap("PruneHelper", map[string]interface{}{"name": i18n.GetMsgByKey(name), "count": DeletedNumber, "size": common.LoadSizeUnit2F(float64(SpaceReclaimed))}))
|
||||||
|
return nil
|
||||||
|
}, nil)
|
||||||
|
go func() {
|
||||||
|
_ = taskItem.Execute()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
|
func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
|
||||||
|
@ -1241,29 +1268,29 @@ func checkImageExist(client *client.Client, imageItem string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkImageLike(client *client.Client, imageName string) bool {
|
func checkImageLike(client *client.Client, imageName string) bool {
|
||||||
if client == nil {
|
if client == nil {
|
||||||
var err error
|
var err error
|
||||||
client, err = docker.NewDockerClient()
|
client, err = docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
images, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, img := range images {
|
for _, img := range images {
|
||||||
for _, tag := range img.RepoTags {
|
for _, tag := range img.RepoTags {
|
||||||
parts := strings.Split(tag, "/")
|
parts := strings.Split(tag, "/")
|
||||||
imageNameWithTag := parts[len(parts)-1]
|
imageNameWithTag := parts[len(parts)-1]
|
||||||
|
|
||||||
if imageNameWithTag == imageName {
|
if imageNameWithTag == imageName {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func pullImages(task *task.Task, client *client.Client, imageName string) error {
|
func pullImages(task *task.Task, client *client.Client, imageName string) error {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
"github.com/1Panel-dev/1Panel/agent/global"
|
||||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||||
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||||
"github.com/docker/docker/api/types/build"
|
"github.com/docker/docker/api/types/build"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
@ -41,7 +42,7 @@ type IImageService interface {
|
||||||
ImageLoad(req dto.ImageLoad) error
|
ImageLoad(req dto.ImageLoad) error
|
||||||
ImageSave(req dto.ImageSave) error
|
ImageSave(req dto.ImageSave) error
|
||||||
ImagePush(req dto.ImagePush) error
|
ImagePush(req dto.ImagePush) error
|
||||||
ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error)
|
ImageRemove(req dto.BatchDelete) error
|
||||||
ImageTag(req dto.ImageTag) error
|
ImageTag(req dto.ImageTag) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,34 +387,44 @@ func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageService) ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error) {
|
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
||||||
report := dto.ContainerPruneReport{}
|
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return report, err
|
return err
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
for _, id := range req.Names {
|
taskItem, err := task.NewTaskWithOps(task.TaskScopeImage, task.TaskDelete, task.TaskScopeContainer, req.TaskID, 1)
|
||||||
imageItem, _, err := client.ImageInspectWithRaw(context.TODO(), id)
|
if err != nil {
|
||||||
if err != nil {
|
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||||
return report, err
|
return err
|
||||||
}
|
|
||||||
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
|
|
||||||
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
|
|
||||||
if strings.Contains(id, "sha256:") {
|
|
||||||
return report, buserr.New("ErrObjectInUsed")
|
|
||||||
}
|
|
||||||
return report, buserr.WithDetail("ErrInUsed", id, nil)
|
|
||||||
}
|
|
||||||
if strings.Contains(err.Error(), "image has dependent") {
|
|
||||||
return report, buserr.New("ErrObjectBeDependent")
|
|
||||||
}
|
|
||||||
return report, err
|
|
||||||
}
|
|
||||||
report.DeletedNumber++
|
|
||||||
report.SpaceReclaimed += int(imageItem.Size)
|
|
||||||
}
|
}
|
||||||
return report, nil
|
|
||||||
|
for _, id := range req.Names {
|
||||||
|
taskItem.AddSubTask(i18n.GetMsgByKey("TaskDelete")+id, func(t *task.Task) error {
|
||||||
|
imageItem, err := client.ImageInspect(context.TODO(), id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
|
||||||
|
if strings.Contains(id, "sha256:") {
|
||||||
|
return buserr.New("ErrObjectInUsed")
|
||||||
|
}
|
||||||
|
return buserr.WithDetail("ErrInUsed", id, nil)
|
||||||
|
}
|
||||||
|
if strings.Contains(err.Error(), "image has dependent") {
|
||||||
|
return buserr.New("ErrObjectBeDependent")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
taskItem.Log(i18n.GetMsgWithMap("ImageRemoveHelper", map[string]interface{}{"name": id, "size": common.LoadSizeUnit2F(float64(imageItem.Size))}))
|
||||||
|
return nil
|
||||||
|
}, nil)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
_ = taskItem.Execute()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatFileSize(fileSize int64) (size string) {
|
func formatFileSize(fileSize int64) (size string) {
|
||||||
|
|
|
@ -65,6 +65,7 @@ const (
|
||||||
TaskPull = "TaskPull"
|
TaskPull = "TaskPull"
|
||||||
TaskCommit = "TaskCommit"
|
TaskCommit = "TaskCommit"
|
||||||
TaskPush = "TaskPush"
|
TaskPush = "TaskPush"
|
||||||
|
TaskClean = "TaskClean"
|
||||||
TaskHandle = "TaskHandle"
|
TaskHandle = "TaskHandle"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,11 @@ ErrObjectInUsed: 'The object is in use and cannot be deleted'
|
||||||
ErrObjectBeDependent: 'This image depends on other images and cannot be deleted'
|
ErrObjectBeDependent: 'This image depends on other images and cannot be deleted'
|
||||||
ErrPortRules: 'Port number does not match, please re-enter!'
|
ErrPortRules: 'Port number does not match, please re-enter!'
|
||||||
ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again'
|
ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again'
|
||||||
|
PruneHelper: "This cleanup removed {{ .name }} {{ .count }} items, freeing {{ .size }} disk space"
|
||||||
|
ImageRemoveHelper: "Deleted image {{ .name }}, freeing {{ .size }} disk space"
|
||||||
|
BuildCache: "Build cache"
|
||||||
|
Volume: "Storage volume"
|
||||||
|
Network: "Network"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrFileNotExist: '{{ .detail }} file does not exist! Please check the integrity of the source file!'
|
ErrFileNotExist: '{{ .detail }} file does not exist! Please check the integrity of the source file!'
|
||||||
|
@ -290,6 +295,7 @@ TaskPull: 'Pull'
|
||||||
TaskCommit: 'Commit'
|
TaskCommit: 'Commit'
|
||||||
TaskBuild: 'Build'
|
TaskBuild: 'Build'
|
||||||
TaskPush: 'Push'
|
TaskPush: 'Push'
|
||||||
|
TaskClean: "Cleanup"
|
||||||
TaskHandle: 'Execute'
|
TaskHandle: 'Execute'
|
||||||
Website: 'Website'
|
Website: 'Website'
|
||||||
App: 'Application'
|
App: 'Application'
|
||||||
|
|
|
@ -173,6 +173,11 @@ ErrObjectInUsed: 'オブジェクトは使用中のため削除できません'
|
||||||
ErrObjectBeDependent: 'このイメージは他のイメージに依存しているため、削除できません'
|
ErrObjectBeDependent: 'このイメージは他のイメージに依存しているため、削除できません'
|
||||||
ErrPortRules: 'ポート番号が一致しません。再入力してください。'
|
ErrPortRules: 'ポート番号が一致しません。再入力してください。'
|
||||||
ErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください'
|
ErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください'
|
||||||
|
PruneHelper: "今回のクリーンアップで{{ .name }} {{ .count }}個を削除し、{{ .size }}のディスク領域を解放しました"
|
||||||
|
ImageRemoveHelper: "イメージ{{ .name }}を削除し、{{ .size }}のディスク領域を解放しました"
|
||||||
|
BuildCache: "ビルドキャッシュ"
|
||||||
|
Volume: "ストレージボリューム"
|
||||||
|
Network: "ネットワーク"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。'
|
ErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。'
|
||||||
|
@ -290,6 +295,7 @@ TaskPull: 'プル'
|
||||||
TaskCommit: 'コミット'
|
TaskCommit: 'コミット'
|
||||||
TaskBuild: 'ビルド'
|
TaskBuild: 'ビルド'
|
||||||
TaskPush: 'プッシュ'
|
TaskPush: 'プッシュ'
|
||||||
|
TaskClean: "クリーンアップ"
|
||||||
TaskHandle: '実行'
|
TaskHandle: '実行'
|
||||||
Website: 'ウェブサイト'
|
Website: 'ウェブサイト'
|
||||||
App: 'アプリケーション'
|
App: 'アプリケーション'
|
||||||
|
|
|
@ -173,6 +173,11 @@ ErrObjectInUsed: '개체가 사용 중이므로 삭제할 수 없습니다'
|
||||||
ErrObjectBeDependent: '이 이미지는 다른 이미지에 의존하므로 삭제할 수 없습니다'
|
ErrObjectBeDependent: '이 이미지는 다른 이미지에 의존하므로 삭제할 수 없습니다'
|
||||||
ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요!'
|
ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요!'
|
||||||
ErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.'
|
ErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.'
|
||||||
|
PruneHelper: "이번 정리에서 {{ .name }} {{ .count }}개를 제거하여 {{ .size }} 디스크 공간을 확보했습니다"
|
||||||
|
ImageRemoveHelper: "이미지 {{ .name }} 삭제, {{ .size }} 디스크 공간 확보"
|
||||||
|
BuildCache: "빌드 캐시"
|
||||||
|
Volume: "스토리지 볼륨"
|
||||||
|
Network: "네트워크"
|
||||||
|
|
||||||
#실행 시간
|
#실행 시간
|
||||||
ErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다! 소스 파일의 무결성을 확인하세요!'
|
ErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다! 소스 파일의 무결성을 확인하세요!'
|
||||||
|
@ -290,6 +295,7 @@ TaskPull: '당기기'
|
||||||
TaskCommit: '커밋'
|
TaskCommit: '커밋'
|
||||||
TaskBuild: '빌드'
|
TaskBuild: '빌드'
|
||||||
TaskPush: '푸시'
|
TaskPush: '푸시'
|
||||||
|
TaskClean: "정리"
|
||||||
TaskHandle: '실행'
|
TaskHandle: '실행'
|
||||||
Website: '웹사이트'
|
Website: '웹사이트'
|
||||||
App: '애플리케이션'
|
App: '애플리케이션'
|
||||||
|
|
|
@ -172,6 +172,11 @@ ErrObjectInUsed: 'Objek sedang digunakan dan tidak boleh dipadamkan'
|
||||||
ErrObjectBeDependent: 'Imej ini bergantung pada imej lain dan tidak boleh dipadamkan'
|
ErrObjectBeDependent: 'Imej ini bergantung pada imej lain dan tidak boleh dipadamkan'
|
||||||
ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula!'
|
ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula!'
|
||||||
ErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi'
|
ErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi'
|
||||||
|
PruneHelper: "Pembersihan ini membuang {{ .name }} {{ .count }} item, membebaskan {{ .size }} ruang cakera"
|
||||||
|
ImageRemoveHelper: "Padam imej {{ .name }}, membebaskan {{ .size }} ruang cakera"
|
||||||
|
BuildCache: "Cache binaan"
|
||||||
|
Volume: "Jilid storan"
|
||||||
|
Network: "Rangkaian"
|
||||||
|
|
||||||
#masa berjalan
|
#masa berjalan
|
||||||
ErrFileNotExist: 'Fail {{ .detail }} tidak wujud! Sila semak integriti fail sumber!'
|
ErrFileNotExist: 'Fail {{ .detail }} tidak wujud! Sila semak integriti fail sumber!'
|
||||||
|
@ -289,6 +294,7 @@ TaskPull: 'Tarik'
|
||||||
TaskCommit: 'Komit'
|
TaskCommit: 'Komit'
|
||||||
TaskBuild: 'Bina'
|
TaskBuild: 'Bina'
|
||||||
TaskPush: 'Tolak'
|
TaskPush: 'Tolak'
|
||||||
|
TaskClean: "Pembersihan"
|
||||||
TaskHandle: 'Laksanakan'
|
TaskHandle: 'Laksanakan'
|
||||||
Website: 'Laman web'
|
Website: 'Laman web'
|
||||||
App: 'Aplikasi'
|
App: 'Aplikasi'
|
||||||
|
|
|
@ -173,6 +173,11 @@ ErrObjectInUsed: 'O objeto está em uso e não pode ser excluído'
|
||||||
ErrObjectBeDependent: 'Esta imagem depende de outras imagens e não pode ser excluída'
|
ErrObjectBeDependent: 'Esta imagem depende de outras imagens e não pode ser excluída'
|
||||||
ErrPortRules: 'O número da porta não corresponde, digite novamente!'
|
ErrPortRules: 'O número da porta não corresponde, digite novamente!'
|
||||||
ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e tente novamente'
|
ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e tente novamente'
|
||||||
|
PruneHelper: "Esta limpeza removeu {{ .name }} {{ .count }} itens, liberando {{ .size }} de espaço em disco"
|
||||||
|
ImageRemoveHelper: "Excluída a imagem {{ .name }}, liberando {{ .size }} de espaço em disco"
|
||||||
|
BuildCache: "Cache de construção"
|
||||||
|
Volume: "Volume de armazenamento"
|
||||||
|
Network: "Rede"
|
||||||
|
|
||||||
#tempo de execução
|
#tempo de execução
|
||||||
ErrFileNotExist: 'O arquivo {{ .detail }} não existe! Verifique a integridade do arquivo de origem!'
|
ErrFileNotExist: 'O arquivo {{ .detail }} não existe! Verifique a integridade do arquivo de origem!'
|
||||||
|
@ -290,6 +295,7 @@ TaskPull: 'Puxar'
|
||||||
TaskCommit: 'Commit'
|
TaskCommit: 'Commit'
|
||||||
TaskBuild: 'Construir'
|
TaskBuild: 'Construir'
|
||||||
TaskPush: 'Empurrar'
|
TaskPush: 'Empurrar'
|
||||||
|
TaskClean: "Limpeza"
|
||||||
TaskHandle: 'Executar'
|
TaskHandle: 'Executar'
|
||||||
Website: 'Site'
|
Website: 'Site'
|
||||||
App: 'Aplicativo'
|
App: 'Aplicativo'
|
||||||
|
|
|
@ -173,6 +173,11 @@ ErrObjectInUsed: 'Объект используется и не может бы
|
||||||
ErrObjectBeDependent: 'Это изображение зависит от других изображений и не может быть удалено'
|
ErrObjectBeDependent: 'Это изображение зависит от других изображений и не может быть удалено'
|
||||||
ErrPortRules: 'Номер порта не совпадает, введите заново!'
|
ErrPortRules: 'Номер порта не совпадает, введите заново!'
|
||||||
ErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку'
|
ErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку'
|
||||||
|
PruneHelper: "Очистка удалила {{ .name }} в количестве {{ .count }}, освободив {{ .size }} дискового пространства"
|
||||||
|
ImageRemoveHelper: "Удалён образ {{ .name }}, освобождено {{ .size }} дискового пространства"
|
||||||
|
BuildCache: "Кэш сборки"
|
||||||
|
Volume: "Том хранилища"
|
||||||
|
Network: "Сеть"
|
||||||
|
|
||||||
#время выполнения
|
#время выполнения
|
||||||
ErrFileNotExist: 'Файл {{ .detail }} не существует! Проверьте целостность исходного файла!'
|
ErrFileNotExist: 'Файл {{ .detail }} не существует! Проверьте целостность исходного файла!'
|
||||||
|
@ -290,6 +295,7 @@ TaskPull: 'Вытянуть'
|
||||||
TaskCommit: 'Kоммит'
|
TaskCommit: 'Kоммит'
|
||||||
ЗадачаСборка: 'Сборка'
|
ЗадачаСборка: 'Сборка'
|
||||||
TaskPush: 'Push'
|
TaskPush: 'Push'
|
||||||
|
TaskClean: "Очистка"
|
||||||
TaskHandle: 'Выполнить'
|
TaskHandle: 'Выполнить'
|
||||||
Website: 'Be6-сайт'
|
Website: 'Be6-сайт'
|
||||||
App: 'Приложение'
|
App: 'Приложение'
|
||||||
|
|
|
@ -173,6 +173,11 @@ ErrObjectInUsed: 'Nesne kullanımda ve silinemez'
|
||||||
ErrObjectBeDependent: 'Bu image diğer imagelere bağlı ve silinemez'
|
ErrObjectBeDependent: 'Bu image diğer imagelere bağlı ve silinemez'
|
||||||
ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin!'
|
ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin!'
|
||||||
ErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin'
|
ErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin'
|
||||||
|
PruneHelper: "Bu temizleme {{ .name }} {{ .count }} öğe kaldırdı, {{ .size }} disk alanı boşalttı"
|
||||||
|
ImageRemoveHelper: "{{ .name }} imajı silindi, {{ .size }} disk alanı boşalttı"
|
||||||
|
BuildCache: "Derleme önbelleği"
|
||||||
|
Volume: "Depolama hacmi"
|
||||||
|
Network: "Ağ"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrFileNotExist: '{{ .detail }} dosyası mevcut değil! Lütfen kaynak dosyanın bütünlüğünü kontrol edin!'
|
ErrFileNotExist: '{{ .detail }} dosyası mevcut değil! Lütfen kaynak dosyanın bütünlüğünü kontrol edin!'
|
||||||
|
@ -288,6 +293,7 @@ TaskPull: 'Çek'
|
||||||
TaskCommit: 'işleme'
|
TaskCommit: 'işleme'
|
||||||
TaskBuild: 'Yapı'
|
TaskBuild: 'Yapı'
|
||||||
TaskPush: 'Gönder'
|
TaskPush: 'Gönder'
|
||||||
|
TaskClean: "Temizleme"
|
||||||
TaskHandle: 'Yürüt'
|
TaskHandle: 'Yürüt'
|
||||||
Website: 'Web Sitesi'
|
Website: 'Web Sitesi'
|
||||||
App: 'Uygulama'
|
App: 'Uygulama'
|
||||||
|
|
|
@ -172,6 +172,11 @@ ErrObjectInUsed: '該物件正被使用,無法刪除'
|
||||||
ErrObjectBeDependent: '此鏡像依賴其他鏡像,無法刪除'
|
ErrObjectBeDependent: '此鏡像依賴其他鏡像,無法刪除'
|
||||||
ErrPortRules: '連接埠數目不匹配,請重新輸入!'
|
ErrPortRules: '連接埠數目不匹配,請重新輸入!'
|
||||||
ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .name }} 鏡像後重試'
|
ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .name }} 鏡像後重試'
|
||||||
|
PruneHelper: "本次清理 {{ .name }} {{ .count }} 個,釋放磁盤空間 {{ .size }}"
|
||||||
|
ImageRemoveHelper: "刪除鏡像 {{ .name }} ,釋放磁盤空間 {{ .size }}"
|
||||||
|
BuildCache: "構建緩存"
|
||||||
|
Volume: "存儲卷"
|
||||||
|
Network: "網絡"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!'
|
ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!'
|
||||||
|
@ -289,6 +294,7 @@ TaskPull: '拉取'
|
||||||
TaskCommit: '制作'
|
TaskCommit: '制作'
|
||||||
TaskBuild: '建置'
|
TaskBuild: '建置'
|
||||||
TaskPush: '推送'
|
TaskPush: '推送'
|
||||||
|
TaskClean: "清理"
|
||||||
TaskHandle: '執行'
|
TaskHandle: '執行'
|
||||||
Website: '網站'
|
Website: '網站'
|
||||||
App: '應用程式'
|
App: '應用程式'
|
||||||
|
|
|
@ -172,6 +172,11 @@ ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||||
ErrObjectBeDependent: "该镜像依赖于其他镜像,无法删除"
|
ErrObjectBeDependent: "该镜像依赖于其他镜像,无法删除"
|
||||||
ErrPortRules: "端口数目不匹配,请重新输入!"
|
ErrPortRules: "端口数目不匹配,请重新输入!"
|
||||||
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
|
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
|
||||||
|
PruneHelper: "本次清理 {{ .name }} {{ .count }} 个,释放磁盘空间 {{ .size }}"
|
||||||
|
ImageRemoveHelper: "删除镜像 {{ .name }} ,释放磁盘空间 {{ .size }}"
|
||||||
|
BuildCache: "构建缓存"
|
||||||
|
Volume: "存储卷"
|
||||||
|
Network: "网络"
|
||||||
|
|
||||||
#runtime
|
#runtime
|
||||||
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
||||||
|
@ -289,6 +294,7 @@ TaskPull: "拉取"
|
||||||
TaskCommit: "制作"
|
TaskCommit: "制作"
|
||||||
TaskBuild: "构建"
|
TaskBuild: "构建"
|
||||||
TaskPush: "推送"
|
TaskPush: "推送"
|
||||||
|
TaskClean: "清理"
|
||||||
TaskHandle: "执行"
|
TaskHandle: "执行"
|
||||||
Website: "网站"
|
Website: "网站"
|
||||||
App: "应用"
|
App: "应用"
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { ReqPage } from '.';
|
import { ReqPage } from '.';
|
||||||
|
|
||||||
export namespace Cronjob {
|
export namespace Cronjob {
|
||||||
|
export interface Search extends ReqPage {
|
||||||
|
info: string;
|
||||||
|
groupIDs: Array<number>;
|
||||||
|
orderBy?: string;
|
||||||
|
order?: string;
|
||||||
|
}
|
||||||
export interface CronjobInfo {
|
export interface CronjobInfo {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -20,7 +20,6 @@ export interface ReqPage {
|
||||||
}
|
}
|
||||||
export interface SearchWithPage {
|
export interface SearchWithPage {
|
||||||
info: string;
|
info: string;
|
||||||
groupIDs: Array<number>;
|
|
||||||
page: number;
|
page: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
orderBy?: string;
|
orderBy?: string;
|
||||||
|
|
|
@ -53,7 +53,7 @@ export const containerOperator = (params: Container.ContainerOperate) => {
|
||||||
return http.post(`/containers/operate`, params);
|
return http.post(`/containers/operate`, params);
|
||||||
};
|
};
|
||||||
export const containerPrune = (params: Container.ContainerPrune) => {
|
export const containerPrune = (params: Container.ContainerPrune) => {
|
||||||
return http.post<Container.ContainerPruneReport>(`/containers/prune`, params);
|
return http.post(`/containers/prune`, params);
|
||||||
};
|
};
|
||||||
export const inspect = (params: Container.ContainerInspect) => {
|
export const inspect = (params: Container.ContainerInspect) => {
|
||||||
return http.post<string>(`/containers/inspect`, params);
|
return http.post<string>(`/containers/inspect`, params);
|
||||||
|
@ -95,7 +95,7 @@ export const imageTag = (params: Container.ImageTag) => {
|
||||||
return http.post(`/containers/image/tag`, params);
|
return http.post(`/containers/image/tag`, params);
|
||||||
};
|
};
|
||||||
export const imageRemove = (params: Container.BatchDelete) => {
|
export const imageRemove = (params: Container.BatchDelete) => {
|
||||||
return http.post<Container.ContainerPruneReport>(`/containers/image/remove`, params);
|
return http.post(`/containers/image/remove`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
// network
|
// network
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { ResPage, SearchWithPage } from '../interface';
|
||||||
import { Cronjob } from '../interface/cronjob';
|
import { Cronjob } from '../interface/cronjob';
|
||||||
import { TimeoutEnum } from '@/enums/http-enum';
|
import { TimeoutEnum } from '@/enums/http-enum';
|
||||||
|
|
||||||
export const searchCronjobPage = (params: SearchWithPage) => {
|
export const searchCronjobPage = (params: Cronjob.Search) => {
|
||||||
return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search`, params);
|
return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,42 +18,45 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</DialogPro>
|
</DialogPro>
|
||||||
|
<TaskLog ref="taskLogRef" width="70%" @close="onSearch" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { containerPrune } from '@/api/modules/container';
|
import { containerPrune } from '@/api/modules/container';
|
||||||
import i18n from '@/lang';
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { computeSize } from '@/utils/util';
|
import { newUUID } from '@/utils/util';
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const open = ref<boolean>(false);
|
const open = ref<boolean>(false);
|
||||||
|
const taskLogRef = ref();
|
||||||
|
|
||||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
const onClean = async () => {
|
const onClean = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
pruneType: 'container',
|
pruneType: 'container',
|
||||||
withTagAll: false,
|
withTagAll: false,
|
||||||
};
|
};
|
||||||
await containerPrune(params)
|
await containerPrune(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(
|
|
||||||
i18n.global.t('container.cleanSuccessWithSpace', [
|
|
||||||
res.data.deletedNumber,
|
|
||||||
computeSize(res.data.spaceReclaimed),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
open.value = false;
|
open.value = false;
|
||||||
emit('search');
|
openTaskLog(params.taskID);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLogRef.value.openWithTaskID(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSearch = () => {
|
||||||
|
emit('search');
|
||||||
|
};
|
||||||
|
|
||||||
const acceptParams = (): void => {
|
const acceptParams = (): void => {
|
||||||
open.value = true;
|
open.value = true;
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #main>
|
<template #main>
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search" :heightDiff="300">
|
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search" :heightDiff="300">
|
||||||
<el-table-column label="ID" prop="id" width="140" show-overflow-tooltip>
|
<el-table-column label="ID" prop="id" width="140">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-text type="primary" class="cursor-pointer" @click="onInspect(row.id)">
|
<el-text type="primary" class="cursor-pointer" @click="onInspect(row.id)">
|
||||||
{{ row.id.replaceAll('sha256:', '').substring(0, 12) }}
|
{{ row.id.replaceAll('sha256:', '').substring(0, 12) }}
|
||||||
|
@ -92,12 +92,13 @@
|
||||||
<Build ref="dialogBuildRef" @search="search" />
|
<Build ref="dialogBuildRef" @search="search" />
|
||||||
<Delete ref="dialogDeleteRef" @search="search" />
|
<Delete ref="dialogDeleteRef" @search="search" />
|
||||||
<Prune ref="dialogPruneRef" @search="search" />
|
<Prune ref="dialogPruneRef" @search="search" />
|
||||||
|
<TaskLog ref="taskLogRef" width="70%" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, computed } from 'vue';
|
import { reactive, ref, computed } from 'vue';
|
||||||
import { dateFormat } from '@/utils/util';
|
import { dateFormat, newUUID } from '@/utils/util';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import Pull from '@/views/container/image/pull/index.vue';
|
import Pull from '@/views/container/image/pull/index.vue';
|
||||||
import Tag from '@/views/container/image/tag/index.vue';
|
import Tag from '@/views/container/image/tag/index.vue';
|
||||||
|
@ -109,13 +110,14 @@ import Delete from '@/views/container/image/delete/index.vue';
|
||||||
import Prune from '@/views/container/image/prune/index.vue';
|
import Prune from '@/views/container/image/prune/index.vue';
|
||||||
import DockerStatus from '@/views/container/docker-status/index.vue';
|
import DockerStatus from '@/views/container/docker-status/index.vue';
|
||||||
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
||||||
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import { searchImage, listImageRepo, imageRemove, inspect, containerPrune } from '@/api/modules/container';
|
import { searchImage, listImageRepo, imageRemove, inspect, containerPrune } from '@/api/modules/container';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
const taskLogRef = ref();
|
||||||
const mobile = computed(() => {
|
const mobile = computed(() => {
|
||||||
return globalStore.isMobile();
|
return globalStore.isMobile();
|
||||||
});
|
});
|
||||||
|
@ -220,13 +222,14 @@ const onOpenBuildCache = () => {
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
pruneType: 'buildcache',
|
pruneType: 'buildcache',
|
||||||
withTagAll: false,
|
withTagAll: false,
|
||||||
};
|
};
|
||||||
await containerPrune(params)
|
await containerPrune(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
openTaskLog(params.taskID);
|
||||||
search();
|
search();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@ -234,6 +237,9 @@ const onOpenBuildCache = () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLogRef.value.openWithTaskID(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
const onOpenload = () => {
|
const onOpenload = () => {
|
||||||
dialogLoadRef.value!.acceptParams();
|
dialogLoadRef.value!.acceptParams();
|
||||||
|
|
|
@ -38,13 +38,14 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</DrawerPro>
|
</DrawerPro>
|
||||||
|
<TaskLog ref="taskLogRef" width="70%" @close="onSearch" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { containerPrune, imageRemove, listAllImage } from '@/api/modules/container';
|
import { containerPrune, imageRemove, listAllImage } from '@/api/modules/container';
|
||||||
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { newUUID } from '@/utils/util';
|
||||||
import { computeSize } from '@/utils/util';
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
|
@ -54,6 +55,7 @@ const loading = ref();
|
||||||
const unTagList = ref([]);
|
const unTagList = ref([]);
|
||||||
const unUsedList = ref([]);
|
const unUsedList = ref([]);
|
||||||
const data = ref([]);
|
const data = ref([]);
|
||||||
|
const taskLogRef = ref();
|
||||||
|
|
||||||
const checkAll = ref(false);
|
const checkAll = ref(false);
|
||||||
const isIndeterminate = ref(false);
|
const isIndeterminate = ref(false);
|
||||||
|
@ -120,6 +122,10 @@ const handleClose = () => {
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSearch = () => {
|
||||||
|
emit('search');
|
||||||
|
};
|
||||||
|
|
||||||
const onClean = async () => {
|
const onClean = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
if (checkAll.value) {
|
if (checkAll.value) {
|
||||||
|
@ -131,41 +137,34 @@ const onClean = async () => {
|
||||||
|
|
||||||
const prune = async () => {
|
const prune = async () => {
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
pruneType: 'image',
|
pruneType: 'image',
|
||||||
withTagAll: scope.value === 'unused',
|
withTagAll: scope.value === 'unused',
|
||||||
};
|
};
|
||||||
await containerPrune(params)
|
await containerPrune(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
MsgSuccess(
|
openTaskLog(params.taskID);
|
||||||
i18n.global.t('container.cleanSuccessWithSpace', [
|
|
||||||
res.data.deletedNumber,
|
|
||||||
computeSize(res.data.spaceReclaimed),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
emit('search');
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLogRef.value.openWithTaskID(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
const removeImage = async () => {
|
const removeImage = async () => {
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
names: checkedLists.value,
|
names: checkedLists.value,
|
||||||
};
|
};
|
||||||
await imageRemove(params)
|
await imageRemove(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
MsgSuccess(
|
openTaskLog(params.taskID);
|
||||||
i18n.global.t('container.cleanSuccessWithSpace', [
|
|
||||||
res.data.deletedNumber,
|
|
||||||
computeSize(res.data.spaceReclaimed),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
emit('search');
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
<OpDialog ref="opRef" @search="search" />
|
<OpDialog ref="opRef" @search="search" />
|
||||||
<CodemirrorDrawer ref="myDetail" />
|
<CodemirrorDrawer ref="myDetail" />
|
||||||
<CreateDialog @search="search" ref="dialogCreateRef" />
|
<CreateDialog @search="search" ref="dialogCreateRef" />
|
||||||
|
<TaskLog ref="taskLogRef" width="70%" @close="search" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -95,16 +96,17 @@
|
||||||
import CreateDialog from '@/views/container/network/create/index.vue';
|
import CreateDialog from '@/views/container/network/create/index.vue';
|
||||||
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { dateFormat } from '@/utils/util';
|
import { dateFormat, newUUID } from '@/utils/util';
|
||||||
import { deleteNetwork, searchNetwork, inspect, containerPrune } from '@/api/modules/container';
|
import { deleteNetwork, searchNetwork, inspect, containerPrune } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
import DockerStatus from '@/views/container/docker-status/index.vue';
|
import DockerStatus from '@/views/container/docker-status/index.vue';
|
||||||
|
|
||||||
const loading = ref();
|
const loading = ref();
|
||||||
const myDetail = ref();
|
const myDetail = ref();
|
||||||
|
const taskLogRef = ref();
|
||||||
|
|
||||||
const data = ref();
|
const data = ref();
|
||||||
const selects = ref<any>([]);
|
const selects = ref<any>([]);
|
||||||
|
@ -136,20 +138,23 @@ const onClean = () => {
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
pruneType: 'network',
|
pruneType: 'network',
|
||||||
withTagAll: false,
|
withTagAll: false,
|
||||||
};
|
};
|
||||||
await containerPrune(params)
|
await containerPrune(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
openTaskLog(params.taskID);
|
||||||
search();
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLogRef.value.openWithTaskID(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
function selectable(row) {
|
function selectable(row) {
|
||||||
return !row.isSystem;
|
return !row.isSystem;
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
|
|
||||||
<CodemirrorDrawer ref="myDetail" />
|
<CodemirrorDrawer ref="myDetail" />
|
||||||
<CreateDialog @search="search" ref="dialogCreateRef" />
|
<CreateDialog @search="search" ref="dialogCreateRef" />
|
||||||
|
<TaskLog ref="taskLogRef" width="70%" @close="search" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -91,16 +92,17 @@ import CreateDialog from '@/views/container/volume/create/index.vue';
|
||||||
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
||||||
import DockerStatus from '@/views/container/docker-status/index.vue';
|
import DockerStatus from '@/views/container/docker-status/index.vue';
|
||||||
import { reactive, ref, computed } from 'vue';
|
import { reactive, ref, computed } from 'vue';
|
||||||
import { computeSize, dateFormat } from '@/utils/util';
|
import { dateFormat, newUUID } from '@/utils/util';
|
||||||
import { deleteVolume, searchVolume, inspect, containerPrune } from '@/api/modules/container';
|
import { deleteVolume, searchVolume, inspect, containerPrune } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
const taskLogRef = ref();
|
||||||
const mobile = computed(() => {
|
const mobile = computed(() => {
|
||||||
return globalStore.isMobile();
|
return globalStore.isMobile();
|
||||||
});
|
});
|
||||||
|
@ -174,25 +176,23 @@ const onClean = () => {
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
pruneType: 'volume',
|
pruneType: 'volume',
|
||||||
withTagAll: false,
|
withTagAll: false,
|
||||||
};
|
};
|
||||||
await containerPrune(params)
|
await containerPrune(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(
|
openTaskLog(params.taskID);
|
||||||
i18n.global.t('container.cleanSuccessWithSpace', [
|
|
||||||
res.data.deletedNumber,
|
|
||||||
computeSize(res.data.spaceReclaimed),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
search();
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLogRef.value.openWithTaskID(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
const batchDelete = async (row: Container.VolumeInfo | null) => {
|
const batchDelete = async (row: Container.VolumeInfo | null) => {
|
||||||
let names = [];
|
let names = [];
|
||||||
|
|
|
@ -125,6 +125,7 @@
|
||||||
<Config ref="configRef" />
|
<Config ref="configRef" />
|
||||||
<Supervisor ref="supervisorRef" />
|
<Supervisor ref="supervisorRef" />
|
||||||
<Terminal ref="terminalRef" />
|
<Terminal ref="terminalRef" />
|
||||||
|
<TaskLog ref="taskLogRef" width="70%" @close="search" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -132,10 +133,10 @@
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { Runtime } from '@/api/interface/runtime';
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
import { DeleteRuntime, RuntimeDeleteCheck, SearchRuntimes } from '@/api/modules/runtime';
|
import { DeleteRuntime, RuntimeDeleteCheck, SearchRuntimes } from '@/api/modules/runtime';
|
||||||
import { dateFormat } from '@/utils/util';
|
import { dateFormat, newUUID } from '@/utils/util';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { containerPrune } from '@/api/modules/container';
|
import { containerPrune } from '@/api/modules/container';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import ExtManagement from './extension-management/index.vue';
|
import ExtManagement from './extension-management/index.vue';
|
||||||
import Extensions from './extension-template/index.vue';
|
import Extensions from './extension-template/index.vue';
|
||||||
|
@ -158,6 +159,7 @@ const mobile = computed(() => {
|
||||||
return globalStore.isMobile();
|
return globalStore.isMobile();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const taskLogRef = ref();
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
cacheSizeKey: 'runtime-page-size',
|
cacheSizeKey: 'runtime-page-size',
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
@ -354,20 +356,23 @@ const onOpenBuildCache = () => {
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = {
|
let params = {
|
||||||
|
taskID: newUUID(),
|
||||||
pruneType: 'buildcache',
|
pruneType: 'buildcache',
|
||||||
withTagAll: false,
|
withTagAll: false,
|
||||||
};
|
};
|
||||||
await containerPrune(params)
|
await containerPrune(params)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
openTaskLog(params.taskID);
|
||||||
search();
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const openTaskLog = (taskID: string) => {
|
||||||
|
taskLogRef.value.openWithTaskID(taskID);
|
||||||
|
};
|
||||||
|
|
||||||
const toFolder = (folder: string) => {
|
const toFolder = (folder: string) => {
|
||||||
router.push({ path: '/hosts/files', query: { path: folder } });
|
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||||
|
|
Loading…
Add table
Reference in a new issue