From 4eb264bea4917c7c8f8e4a03337baca5fc2dad2a Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:18:20 +0800 Subject: [PATCH] feat: Support batch image pulling (#10532) Refs #10521 --- agent/app/dto/image.go | 6 +-- agent/app/service/image.go | 18 +++++--- frontend/src/api/interface/container.ts | 2 +- frontend/src/lang/modules/en.ts | 2 + frontend/src/lang/modules/es-es.ts | 2 + frontend/src/lang/modules/ja.ts | 1 + frontend/src/lang/modules/ko.ts | 1 + frontend/src/lang/modules/ms.ts | 2 + frontend/src/lang/modules/pt-br.ts | 2 + frontend/src/lang/modules/ru.ts | 2 + frontend/src/lang/modules/tr.ts | 2 + frontend/src/lang/modules/zh-Hant.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + .../src/views/container/image/pull/index.vue | 45 +++++++++++++------ 14 files changed, 63 insertions(+), 24 deletions(-) diff --git a/agent/app/dto/image.go b/agent/app/dto/image.go index b89400b29..211157bd7 100644 --- a/agent/app/dto/image.go +++ b/agent/app/dto/image.go @@ -30,9 +30,9 @@ type ImageBuild struct { } type ImagePull struct { - TaskID string `json:"taskID"` - RepoID uint `json:"repoID"` - ImageName string `json:"imageName" validate:"required"` + TaskID string `json:"taskID"` + RepoID uint `json:"repoID"` + ImageName []string `json:"imageName" validate:"required"` } type ImageTag struct { diff --git a/agent/app/service/image.go b/agent/app/service/image.go index 802117314..03e6d5c96 100644 --- a/agent/app/service/image.go +++ b/agent/app/service/image.go @@ -256,17 +256,19 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error { return err } defer client.Close() - imageItemName := strings.ReplaceAll(path.Base(req.ImageName), ":", "_") - taskItem, err := task.NewTaskWithOps(imageItemName, task.TaskPull, task.TaskScopeImage, req.TaskID, 1) + taskItem, err := task.NewTaskWithOps(strings.Join(req.ImageName, ","), task.TaskPull, task.TaskScopeImage, req.TaskID, 1) if err != nil { return fmt.Errorf("new task for image pull failed, err: %v", err) } - go func() { - taskItem.AddSubTask(i18n.GetWithName("ImagePull", req.ImageName), func(t *task.Task) error { + + for _, item := range req.ImageName { + itemName := strings.ReplaceAll(path.Base(item), ":", "_") + taskItem.AddSubTask(i18n.GetWithName("ImagePull", itemName), func(t *task.Task) error { + taskItem.Logf("----------------- %s -----------------", itemName) options := image.PullOptions{} - imageName := req.ImageName + imageName := item if req.RepoID == 0 { - hasAuth, authStr := loadAuthInfo(req.ImageName) + hasAuth, authStr := loadAuthInfo(item) if hasAuth { options.RegistryAuth = authStr } @@ -288,7 +290,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error { authStr := base64.URLEncoding.EncodeToString(encodedJSON) options.RegistryAuth = authStr } - imageName = repo.DownloadUrl + "/" + req.ImageName + imageName = repo.DownloadUrl + "/" + item } dockerCli := docker.NewClientWithExist(client) err = dockerCli.PullImageWithProcessAndOptions(taskItem, imageName, options) @@ -298,6 +300,8 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error { } return nil }, nil) + } + go func() { _ = taskItem.Execute() }() return nil diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index c97af1457..20bd13ec5 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -187,7 +187,7 @@ export namespace Container { export interface ImagePull { taskID: string; repoID: number; - imageName: string; + imageName: Array; } export interface ImageTag { sourceID: string; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index c3c7b950d..fec9e3168 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -875,6 +875,8 @@ const message = { image: 'Image | Images', imagePull: 'Pull', + imagePullHelper: + 'Supports selecting multiple images to pull, press Enter after entering each image to continue', imagePush: 'Push', imagePushHelper: 'Detected that this image has multiple tags. Please confirm that the image name used for pushing is: {0}', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 7c6e5fc94..6255b3979 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -877,6 +877,8 @@ const message = { cache: 'Caché', image: 'Imagen | Imágenes', imagePull: 'Descargar', + imagePullHelper: + 'Admite seleccionar múltiples imágenes para descargar, presione Enter después de ingresar cada imagen para continuar', imagePush: 'Subir', imagePushHelper: 'Detected that this image has multiple tags. Please confirm that the image name used for pushing is: {0}', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 43018376f..8338493b6 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -852,6 +852,7 @@ const message = { image: '画像|画像', imagePull: '引く', + imagePullHelper: '複数のイメージの選択をサポートし、各イメージ入力後にEnterキーを押して続行します', imagePush: '押す', imagePushHelper: 'このイメージに複数のタグが存在することが検出されました。プッシュ時に使用するイメージ名が以下であることを確認してください:{0}', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 7b8520b87..8fc7eb178 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -844,6 +844,7 @@ const message = { cache: '캐시', image: '이미지 | 이미지들', imagePull: '풀', + imagePullHelper: '여러 이미지 선택 풀링을 지원하며, 각 이미지 입력 후 Enter 키를 눌러 계속합니다', imagePush: '푸시', imagePushHelper: '이 이미지에 여러 태그가 있는 것으로 감지되었습니다. 푸시 시 사용할 이미지 이름이 다음인지 확인하세요: {0}', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index ab75a47e8..fa4eb8069 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -868,6 +868,8 @@ const message = { image: 'Imej | Imej-imej', imagePull: 'Tarik', + imagePullHelper: + 'Menyokong pemilihan berbilang imej untuk ditarik, tekan Enter selepas memasukkan setiap imej untuk teruskan', imagePush: 'Tekan', imagePushHelper: 'Terdapat pengesahan bahawa imej ini mempunyai beberapa tag. Sila pastikan nama imej yang digunakan untuk menolak adalah: {0}', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 385510ffb..90a7c7104 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -863,6 +863,8 @@ const message = { cache: 'Cache', image: 'Imagem | Imagens', imagePull: 'Puxar', + imagePullHelper: + 'Suporta selecionar múltiplas imagens para puxar, pressione Enter após inserir cada imagem para continuar', imagePush: 'Enviar', imagePushHelper: 'Detectado que esta imagem possui múltiplas tags. Por favor, confirme que o nome da imagem usada para push é: {0}', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index bde73a4f9..d57edf8f6 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -865,6 +865,8 @@ const message = { image: 'Образ | Образы', imagePull: 'Загрузить', + imagePullHelper: + 'Поддерживает выбор нескольких образов для загрузки, нажмите Enter после ввода каждого образа для продолжения', imagePush: 'Отправить', imagePushHelper: 'Обнаружено, что у этого образа несколько тегов. Подтвердите, что имя образа, используемое для отправки: {0}', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index ee7011420..21b7cf882 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -884,6 +884,8 @@ const message = { image: 'İmaj | İmajlar', imagePull: 'Çek', + imagePullHelper: + 'Birden fazla görüntü seçmeyi destekler, her görüntü girdikten sonra Entera basarak devam edin', imagePush: 'Gönder', imagePushHelper: 'Bu imgenin birden fazla etiketi olduğu tespit edildi. Lütfen gönderimde kullanılan imge adının şu olduğunu onaylayın: {0}', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 6c97e9b30..2bda76e39 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -840,6 +840,7 @@ const message = { image: '鏡像', imagePull: '拉取鏡像', + imagePullHelper: '支援選擇拉取多個鏡像,輸入一組鏡像後回車繼續', imagePush: '推送鏡像', imagePushHelper: '檢測到該映像存在多個標籤,請確認推送時使用的映像名稱為:{0}', imageDelete: '刪除鏡像', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 8a67844e5..27a048c16 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -839,6 +839,7 @@ const message = { image: '镜像', imagePull: '拉取镜像', + imagePullHelper: '支持选择拉取多个镜像,输入一组镜像后回车继续', imagePush: '推送镜像', imagePushHelper: '检测到该镜像存在多个标签,请确认推送时使用的镜像名称为:{0}', imageDelete: '删除镜像', diff --git a/frontend/src/views/container/image/pull/index.vue b/frontend/src/views/container/image/pull/index.vue index 6f349ece9..5f979fa06 100644 --- a/frontend/src/views/container/image/pull/index.vue +++ b/frontend/src/views/container/image/pull/index.vue @@ -1,25 +1,21 @@