Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion agent/app/api/v2/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,31 @@ func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
// @Summary List containers
// @Accept json
// @Produce json
// @Success 200 {array} string
// @Success 200 {array} dto.ContainerOptions
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/list [post]
func (b *BaseApi) ListContainer(c *gin.Context) {
helper.SuccessWithData(c, containerService.List())
}

// @Tags Container
// @Summary List containers by image
// @Accept json
// @Produce json
// @Success 200 {array} dto.ContainerOptions
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/list/byimage [post]
func (b *BaseApi) ListContainerByImage(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

helper.SuccessWithData(c, containerService.ListByImage(req.Name))
}

// @Tags Container
// @Summary Load containers status
// @Accept json
Expand Down
8 changes: 4 additions & 4 deletions agent/app/dto/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ type ContainerCreateByCommand struct {
}

type ContainerUpgrade struct {
TaskID string `json:"taskID"`
Name string `json:"name" validate:"required"`
Image string `json:"image" validate:"required"`
ForcePull bool `json:"forcePull"`
TaskID string `json:"taskID"`
Names []string `json:"names" validate:"required"`
Image string `json:"image" validate:"required"`
ForcePull bool `json:"forcePull"`
}

type ContainerListStats struct {
Expand Down
139 changes: 105 additions & 34 deletions agent/app/service/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type ContainerService struct{}
type IContainerService interface {
Page(req dto.PageContainer) (int64, interface{}, error)
List() []dto.ContainerOptions
ListByImage(imageName string) []dto.ContainerOptions
LoadStatus() (dto.ContainerStatus, error)
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
ListNetwork() ([]dto.Options, error)
Expand Down Expand Up @@ -246,6 +247,33 @@ func (u *ContainerService) List() []dto.ContainerOptions {
return options
}

func (u *ContainerService) ListByImage(imageName string) []dto.ContainerOptions {
var options []dto.ContainerOptions
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("load docker client for contianer list failed, err: %v", err)
return nil
}
defer client.Close()
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
global.LOG.Errorf("load container list failed, err: %v", err)
return nil
}
for _, container := range containers {
if container.Image != imageName {
continue
}
for _, name := range container.Names {
if len(name) != 0 {
options = append(options, dto.ContainerOptions{Name: strings.TrimPrefix(name, "/"), State: container.State})
}
}
}

return options
}

func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
var data dto.ContainerStatus
client, err := docker.NewDockerClient()
Expand Down Expand Up @@ -683,6 +711,12 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
return err
}

if len(oldContainer.Config.Entrypoint) != 0 {
if oldContainer.Config.Entrypoint[0] == "/docker-entrypoint.sh" {
oldContainer.Config.Entrypoint = []string{}
}
}

taskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpdate, task.TaskScopeContainer, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("new task for create container failed, err: %v", err)
Expand Down Expand Up @@ -745,17 +779,14 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
}
defer client.Close()
ctx := context.Background()
oldContainer, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
return err
}
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpgrade, task.TaskScopeContainer, req.TaskID, 1)
taskItem, err := task.NewTaskWithOps(req.Image, task.TaskUpgrade, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("new task for create container failed, err: %v", err)
return err
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("ContainerImagePull", req.Image), func(t *task.Task) error {
taskItem.LogStart(i18n.GetWithName("ContainerImagePull", req.Image))
if !checkImageExist(client, req.Image) || req.ForcePull {
if err := pullImages(taskItem, client, req.Image); err != nil {
if !req.ForcePull {
Expand All @@ -766,38 +797,58 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
}
return nil
}, nil)

taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
config := oldContainer.Config
config.Image = req.Image
hostConf := oldContainer.HostConfig
var networkConf network.NetworkingConfig
if oldContainer.NetworkSettings != nil {
for networkKey := range oldContainer.NetworkSettings.Networks {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
break
for _, item := range req.Names {
var oldContainer container.InspectResponse
taskItem.AddSubTask(i18n.GetWithName("ContainerLoadInfo", item), func(t *task.Task) error {
taskItem.Logf("----------------- %s -----------------", item)
oldContainer, err = client.ContainerInspect(ctx, item)
if err != nil {
return err
}
inspected, err := client.ImageInspect(ctx, req.Image)
if err != nil {
return fmt.Errorf("inspect image failed, err: %v", err)
}
if isDynamicImage(inspected) {
oldContainer.Config.Entrypoint = nil
oldContainer.Config.Cmd = nil
}
return nil
}, nil)

taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", item), func(t *task.Task) error {
oldContainer.Config.Cmd = nil
config := oldContainer.Config
config.Image = req.Image
hostConf := oldContainer.HostConfig
var networkConf network.NetworkingConfig
if oldContainer.NetworkSettings != nil {
for networkKey := range oldContainer.NetworkSettings.Networks {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
break
}
}
err := client.ContainerRemove(ctx, item, container.RemoveOptions{Force: true})
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", item), err)
if err != nil {
return err
}
}
err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true})
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", req.Name), err)
if err != nil {
return err
}

con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
if err != nil {
taskItem.Log(i18n.GetMsgByKey("ContainerRecreate"))
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("upgrade container failed, err: %v", err)
}
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
if err != nil {
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
}
return nil
}, nil)
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, item)
if err != nil {
taskItem.Log(i18n.GetMsgByKey("ContainerRecreate"))
reCreateAfterUpdate(item, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("upgrade container failed, err: %v", err)
}
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
if err != nil {
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
}
return nil
}, nil)

}
if err := taskItem.Execute(); err != nil {
global.LOG.Error(err.Error())
}
Expand Down Expand Up @@ -1692,3 +1743,23 @@ func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
}
return exposedPorts
}

func isDynamicImage(inspected image.InspectResponse) bool {
if len(inspected.Config.Entrypoint) > 0 {
entrypointStr := strings.Join(inspected.Config.Entrypoint, " ")
if strings.Contains(entrypointStr, "entrypoint") {
return true
}
}

dirs := []string{"/docker-entrypoint.d", "/docker-entrypoint-initdb.d"}
for _, dir := range dirs {
for _, layer := range inspected.RootFS.Layers {
if strings.Contains(layer, dir) {
return true
}
}
}

return false
}
1 change: 1 addition & 0 deletions agent/router/ro_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.POST("/info", baseApi.ContainerInfo)
baRouter.POST("/search", baseApi.SearchContainer)
baRouter.POST("/list", baseApi.ListContainer)
baRouter.POST("/list/byimage", baseApi.ListContainerByImage)
baRouter.GET("/status", baseApi.LoadContainerStatus)
baRouter.GET("/list/stats", baseApi.ContainerListStats)
baRouter.GET("/search/log", baseApi.ContainerStreamLogs)
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/api/interface/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export namespace Container {

imageSize: number;
}
export interface ContainerOption {
name: string;
state: string;
}
export interface ResourceLimit {
cpu: number;
memory: number;
Expand Down Expand Up @@ -86,7 +90,7 @@ export namespace Container {
}
export interface ContainerUpgrade {
taskID: string;
name: string;
names: Array<string>;
image: string;
forcePull: boolean;
}
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/api/modules/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export const searchContainer = (params: Container.ContainerSearch) => {
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params, TimeoutEnum.T_40S);
};
export const listContainer = () => {
return http.post<Array<Container.ContainerInfo>>(`/containers/list`, {});
return http.post<Array<Container.ContainerOption>>(`/containers/list`, {});
};
export const listContainerByImage = (image: string) => {
return http.post<Array<Container.ContainerOption>>(`/containers/list/byimage`, { name: image });
};
export const loadContainerUsers = (name: string) => {
return http.post<Array<string>>(`/containers/users`, { name: name });
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,8 @@ const message = {
upgradeWarning2:
'The upgrade operation requires rebuilding the container, any unpersisted data will be lost. Do you wish to continue?',
oldImage: 'Current image',
sameImageContainer: 'Same-image containers',
sameImageHelper: 'Containers using the same image can be batch upgraded after selection',
targetImage: 'Target image',
imageLoadErr: 'No image name detected for the container',
appHelper: 'The container comes from the app store, and upgrading may make the service unavailable.',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,8 @@ const message = {
upgradeWarning2: 'アップグレード操作では、コンテナを再構築する必要があります。続けたいですか?',
oldImage: '現在の画像',
targetImage: 'ターゲット画像',
sameImageContainer: '同一イメージコンテナ',
sameImageHelper: '同一イメージを使用するコンテナは選択後一括アップグレード可能',
imageLoadErr: 'コンテナの画像名は検出されません',
appHelper:
'このコンテナはアプリストアから取得されたものであり、アップグレードによってサービスが利用不可になる可能性があります。',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,8 @@ const message = {
upgradeWarning2:
'업그레이드 작업은 컨테이너를 재빌드해야 하며, 비지속적인 데이터가 손실됩니다. 계속하시겠습니까?',
oldImage: '현재 이미지',
sameImageContainer: '동일 이미지 컨테이너',
sameImageHelper: '동일한 이미지를 사용하는 컨테이너는 선택 후 일괄 업그레이드 가능',
targetImage: '대상 이미지',
imageLoadErr: '컨테이너에 대한 이미지 이름이 감지되지 않았습니다.',
appHelper: '이 컨테이너는 앱 스토어에서 왔으며 업그레이드 시 서비스가 중단될 수 있습니다.',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,8 @@ const message = {
upgradeWarning2:
'Operasi peningkatan memerlukan pembinaan semula kontena, sebarang data yang tidak disimpan akan hilang. Adakah anda mahu meneruskan?',
oldImage: 'Imej semasa',
sameImageContainer: 'Kontena imej sama',
sameImageHelper: 'Kontena yang menggunakan imej sama boleh dinaik taraf secara berkumpulan setelah dipilih',
targetImage: 'Imej sasaran',
imageLoadErr: 'Tiada nama imej dikesan untuk kontena',
appHelper:
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/pt-br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,8 @@ const message = {
upgradeWarning2:
'A operação de upgrade requer a reconstrução do contêiner, e qualquer dado não persistente será perdido. Deseja continuar?',
oldImage: 'Imagem atual',
sameImageContainer: 'Contêineres com mesma imagem',
sameImageHelper: 'Contêineres usando a mesma imagem podem ser atualizados em lote após seleção',
targetImage: 'Imagem alvo',
imageLoadErr: 'Nenhum nome de imagem detectado para o contêiner',
appHelper: 'O contêiner vem da loja de aplicativos, e o upgrade pode tornar o serviço indisponível.',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,8 @@ const message = {
upgradeWarning2:
'Операция обновления требует пересборки контейнера, все несохраненные данные будут потеряны. Хотите продолжить?',
oldImage: 'Текущий образ',
sameImageContainer: 'Контейнеры с одинаковым образом',
sameImageHelper: 'Контейнеры, использующие один образ, можно массово обновить после выбора',
targetImage: 'Целевой образ',
imageLoadErr: 'Не обнаружено имя образа для контейнера',
appHelper: 'Контейнер происходит из магазина приложений, и обновление может сделать сервис недоступным.',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,8 @@ const message = {
upgradeWarning2:
'Yükseltme işlemi konteynerin yeniden oluşturulmasını gerektirir, kalıcı olmayan tüm veriler kaybedilecektir. Devam etmek istiyor musunuz?',
oldImage: 'Mevcut imaj',
sameImageContainer: 'Aynı imajlı konteynerler',
sameImageHelper: 'Aynı imajı kullanan konteynerlar seçilerek toplu şekilde güncellenebilir',
targetImage: 'Hedef imaj',
imageLoadErr: 'Konteyner için imaj adı algılanmadı',
appHelper: 'Konteyner uygulama mağazasından geliyor ve yükseltme hizmeti kullanılamaz hale getirebilir.',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/zh-Hant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ const message = {
upgradeHelper: '倉庫名稱/鏡像名稱:鏡像版本',
upgradeWarning2: '升級操作需要重建容器,任何未持久化的數據將會丟失,是否繼續?',
oldImage: '當前鏡像',
sameImageContainer: '同鏡像容器',
sameImageHelper: '同鏡像容器可勾選後批量升級',
targetImage: '目標鏡像',
imageLoadErr: '未檢測到容器的鏡像名稱',
appHelper: '該容器來源於應用商店,升級可能導致該服務不可用',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ const message = {
upgradeHelper: '仓库名称/镜像名称:镜像版本',
upgradeWarning2: '升级操作需要重建容器,任何未持久化的数据将会丢失,是否继续?',
oldImage: '当前镜像',
sameImageContainer: '同镜像容器',
sameImageHelper: '同镜像容器可勾选后批量升级',
targetImage: '目标镜像',
imageLoadErr: '未检测到容器的镜像名称',
appHelper: '该容器来源于应用商店,升级可能导致该服务不可用',
Expand Down
Loading
Loading