From 3ead6333436513bd40519b73b0407063f2498859 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Fri, 16 Jun 2023 17:54:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E5=8D=87=E7=BA=A7=E6=93=8D=E4=BD=9C=20(#1393?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/container.go | 26 ++++ backend/app/dto/container.go | 9 +- backend/app/dto/image.go | 20 +-- backend/app/service/container.go | 46 +++++- backend/app/service/snapshot.go | 2 +- backend/router/ro_container.go | 1 + cmd/server/docs/docs.go | 60 +++++++- cmd/server/docs/swagger.json | 60 +++++++- cmd/server/docs/swagger.yaml | 40 ++++- frontend/src/api/modules/container.ts | 3 + frontend/src/lang/modules/en.ts | 11 +- frontend/src/lang/modules/zh.ts | 9 +- .../src/views/container/container/index.vue | 119 ++++++++++++--- .../container/container/operate/index.vue | 23 +-- .../container/container/upgrade/index.vue | 141 ++++++++++++++++++ 15 files changed, 514 insertions(+), 56 deletions(-) create mode 100644 frontend/src/views/container/container/upgrade/index.vue diff --git a/backend/app/api/v1/container.go b/backend/app/api/v1/container.go index b9482a504..9e96a884f 100644 --- a/backend/app/api/v1/container.go +++ b/backend/app/api/v1/container.go @@ -245,6 +245,32 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) { helper.SuccessWithData(c, nil) } +// @Tags Container +// @Summary Upgrade container +// @Description 更新容器镜像 +// @Accept json +// @Param request body dto.ContainerUpgrade true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /containers/upgrade [post] +// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新容器镜像 [name][image]","formatEN":"upgrade container image [name][image]"} +func (b *BaseApi) ContainerUpgrade(c *gin.Context) { + var req dto.ContainerUpgrade + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := containerService.ContainerUpgrade(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + // @Tags Container // @Summary Clean container // @Description 容器清理 diff --git a/backend/app/dto/container.go b/backend/app/dto/container.go index 0d017474d..37e493ed9 100644 --- a/backend/app/dto/container.go +++ b/backend/app/dto/container.go @@ -41,7 +41,7 @@ type ContainerOperate struct { PublishAllPorts bool `json:"publishAllPorts"` ExposedPorts []PortHelper `json:"exposedPorts"` Cmd []string `json:"cmd"` - CPUShares int64 `josn:"cpuShares"` + CPUShares int64 `json:"cpuShares"` NanoCPUs int64 `json:"nanoCPUs"` Memory int64 `json:"memory"` AutoRemove bool `json:"autoRemove"` @@ -51,6 +51,11 @@ type ContainerOperate struct { RestartPolicy string `json:"restartPolicy"` } +type ContainerUpgrade struct { + Name string `json:"name" validate:"required"` + Image string `json:"image" validate:"required"` +} + type ContainterStats struct { CPUPercent float64 `json:"cpuPercent"` Memory float64 `json:"memory"` @@ -83,7 +88,7 @@ type ContainerOperation struct { type ContainerPrune struct { PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"` - WithTagAll bool `josn:"withTagAll"` + WithTagAll bool `json:"withTagAll"` } type ContainerPruneReport struct { diff --git a/backend/app/dto/image.go b/backend/app/dto/image.go index 4433c3400..9770e3879 100644 --- a/backend/app/dto/image.go +++ b/backend/app/dto/image.go @@ -10,35 +10,35 @@ type ImageInfo struct { } type ImageLoad struct { - Path string `josn:"path" validate:"required"` + Path string `json:"path" validate:"required"` } type ImageBuild struct { - From string `josn:"from" validate:"required"` + From string `json:"from" validate:"required"` Name string `json:"name" validate:"required"` - Dockerfile string `josn:"dockerfile" validate:"required"` - Tags []string `josn:"tags"` + Dockerfile string `json:"dockerfile" validate:"required"` + Tags []string `json:"tags"` } type ImagePull struct { - RepoID uint `josn:"repoID"` - ImageName string `josn:"imageName" validate:"required"` + RepoID uint `json:"repoID"` + ImageName string `json:"imageName" validate:"required"` } type ImageTag struct { - RepoID uint `josn:"repoID"` + RepoID uint `json:"repoID"` SourceID string `json:"sourceID" validate:"required"` - TargetName string `josn:"targetName" validate:"required"` + TargetName string `json:"targetName" validate:"required"` } type ImagePush struct { - RepoID uint `josn:"repoID" validate:"required"` + RepoID uint `json:"repoID" validate:"required"` TagName string `json:"tagName" validate:"required"` Name string `json:"name" validate:"required"` } type ImageSave struct { TagName string `json:"tagName" validate:"required"` - Path string `josn:"path" validate:"required"` + Path string `json:"path" validate:"required"` Name string `json:"name" validate:"required"` } diff --git a/backend/app/service/container.go b/backend/app/service/container.go index c4aa0a87f..79f43cf7a 100644 --- a/backend/app/service/container.go +++ b/backend/app/service/container.go @@ -43,6 +43,7 @@ type IContainerService interface { ComposeOperation(req dto.ComposeOperation) error ContainerCreate(req dto.ContainerOperate) error ContainerUpdate(req dto.ContainerOperate) error + ContainerUpgrade(req dto.ContainerUpgrade) error ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error) LoadResouceLimit() (*dto.ResourceLimit, error) ContainerLogClean(req dto.OperationWithName) error @@ -241,9 +242,9 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error { return err } - var config *container.Config - var hostConf *container.HostConfig - if err := loadConfigInfo(req, config, hostConf); err != nil { + var config container.Config + var hostConf container.HostConfig + if err := loadConfigInfo(req, &config, &hostConf); err != nil { return err } @@ -255,7 +256,7 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error { return err } } - container, err := client.ContainerCreate(ctx, config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name) + container, err := client.ContainerCreate(ctx, &config, &hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name) if err != nil { _ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true}) return err @@ -284,6 +285,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai data.Image = oldContainer.Config.Image data.Cmd = oldContainer.Config.Cmd data.Env = oldContainer.Config.Env + data.CPUShares = oldContainer.HostConfig.CPUShares for key, val := range oldContainer.Config.Labels { data.Labels = append(data.Labels, fmt.Sprintf("%s=%s", key, val)) } @@ -357,6 +359,41 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error { return nil } +func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error { + client, err := docker.NewDockerClient() + if err != nil { + return err + } + ctx := context.Background() + oldContainer, err := client.ContainerInspect(ctx, req.Name) + if err != nil { + return err + } + if !checkImageExist(client, req.Image) { + if err := pullImages(ctx, client, req.Image); err != nil { + return err + } + } + config := oldContainer.Config + config.Image = req.Image + hostConf := oldContainer.HostConfig + if err := client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{Force: true}); err != nil { + return err + } + + global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name) + container, err := client.ContainerCreate(ctx, config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name) + if err != nil { + return fmt.Errorf("recreate contianer failed, err: %v", err) + } + global.LOG.Infof("update container %s successful! now check if the container is started.", req.Name) + if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil { + return fmt.Errorf("update successful but start failed, err: %v", err) + } + + return nil +} + func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error { var err error ctx := context.Background() @@ -648,6 +685,7 @@ func loadConfigInfo(req dto.ContainerOperate, config *container.Config, hostConf config.ExposedPorts = exposeds hostConf.AutoRemove = req.AutoRemove + hostConf.CPUShares = req.CPUShares hostConf.PublishAllPorts = req.PublishAllPorts hostConf.RestartPolicy = container.RestartPolicy{Name: req.RestartPolicy} if req.RestartPolicy == "on-failure" { diff --git a/backend/app/service/snapshot.go b/backend/app/service/snapshot.go index 1844d9ad1..c3c869259 100644 --- a/backend/app/service/snapshot.go +++ b/backend/app/service/snapshot.go @@ -535,7 +535,7 @@ func (u *SnapshotService) handleDaemonJson(fileOp files.FileOp, operation string if operation == "snapshot" || operation == "recover" { _, err := os.Stat(daemonJsonPath) if os.IsNotExist(err) { - global.LOG.Info("no daemon.josn in snapshot and system now, nothing happened") + global.LOG.Info("no daemon.json in snapshot and system now, nothing happened") } if err == nil { if err := fileOp.CopyFile(daemonJsonPath, target); err != nil { diff --git a/backend/router/ro_container.go b/backend/router/ro_container.go index 23cc6dc95..f99fbda3b 100644 --- a/backend/router/ro_container.go +++ b/backend/router/ro_container.go @@ -20,6 +20,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.POST("", baseApi.ContainerCreate) baRouter.POST("/update", baseApi.ContainerUpdate) + baRouter.POST("/upgrade", baseApi.ContainerUpgrade) baRouter.POST("/info", baseApi.ContainerInfo) baRouter.POST("/search", baseApi.SearchContainer) baRouter.GET("/search/log", baseApi.ContainerLogs) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index c31a7a2c1..73a1298c8 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -2695,6 +2695,49 @@ var doc = `{ } } }, + "/containers/upgrade": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "更新容器镜像", + "consumes": [ + "application/json" + ], + "tags": [ + "Container" + ], + "summary": "Upgrade container", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ContainerUpgrade" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "name", + "image" + ], + "formatEN": "upgrade container image [name][image]", + "formatZH": "更新容器镜像 [name][image]", + "paramKeys": [] + } + } + }, "/containers/volume": { "post": { "security": [ @@ -10694,7 +10737,7 @@ var doc = `{ "type": "string" } }, - "cpushares": { + "cpuShares": { "type": "integer" }, "env": { @@ -10800,6 +10843,21 @@ var doc = `{ } } }, + "dto.ContainerUpgrade": { + "type": "object", + "required": [ + "image", + "name" + ], + "properties": { + "image": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, "dto.ContainterStats": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 34804055d..55df7c9d8 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -2681,6 +2681,49 @@ } } }, + "/containers/upgrade": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "更新容器镜像", + "consumes": [ + "application/json" + ], + "tags": [ + "Container" + ], + "summary": "Upgrade container", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ContainerUpgrade" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "name", + "image" + ], + "formatEN": "upgrade container image [name][image]", + "formatZH": "更新容器镜像 [name][image]", + "paramKeys": [] + } + } + }, "/containers/volume": { "post": { "security": [ @@ -10680,7 +10723,7 @@ "type": "string" } }, - "cpushares": { + "cpuShares": { "type": "integer" }, "env": { @@ -10786,6 +10829,21 @@ } } }, + "dto.ContainerUpgrade": { + "type": "object", + "required": [ + "image", + "name" + ], + "properties": { + "image": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, "dto.ContainterStats": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 18b0dd483..a80241a75 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -261,7 +261,7 @@ definitions: items: type: string type: array - cpushares: + cpuShares: type: integer env: items: @@ -334,6 +334,16 @@ definitions: spaceReclaimed: type: integer type: object + dto.ContainerUpgrade: + properties: + image: + type: string + name: + type: string + required: + - image + - name + type: object dto.ContainterStats: properties: cache: @@ -5070,6 +5080,34 @@ paths: formatEN: update container [name][image] formatZH: 更新容器 [name][image] paramKeys: [] + /containers/upgrade: + post: + consumes: + - application/json + description: 更新容器镜像 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/dto.ContainerUpgrade' + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Upgrade container + tags: + - Container + x-panel-log: + BeforeFuntions: [] + bodyKeys: + - name + - image + formatEN: upgrade container image [name][image] + formatZH: 更新容器镜像 [name][image] + paramKeys: [] /containers/volume: post: consumes: diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 88ce65e66..f1c7c89f2 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -14,6 +14,9 @@ export const createContainer = (params: Container.ContainerHelper) => { export const updateContainer = (params: Container.ContainerHelper) => { return http.post(`/containers/update`, params, 3000000); }; +export const upgradeContainer = (name: string, image: string) => { + return http.post(`/containers/upgrade`, { name: name, image: image }, 3000000); +}; export const loadContainerInfo = (name: string) => { return http.post(`/containers/info`, { name: name }); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 28fc8af4d..bcff6b0a8 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -491,11 +491,20 @@ const message = { user: 'User', command: 'Command', - commandHelper: 'Please enter the correct command, separated by spaces if there are multiple commands.', custom: 'Custom', emptyUser: 'When empty, you will log in as default', containerTerminal: 'Terminal', + upgrade: 'Upgrade', + upgradeHelper: 'This operation only supports upgrading container versions.', + upgradeWarning: 'The target version is lower than the original image version. Please try again!', + upgradeWarning2: + 'The upgrade operation requires rebuilding the container, and any non-persistent data will be lost. Do you want to continue?', + oldImage: 'Current image', + targetImage: 'Target image', + appHelper: + 'This container is sourced from the application store. Upgrading it may cause the service to be unavailable. Do you want to continue?', + port: 'Port', server: 'Host', serverExample: 'e.g. 80, 80-88, ip:80 or ip:80-88', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index db6aa295a..10f67e2a0 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -496,11 +496,18 @@ const message = { user: '用户', command: '命令', - commandHelper: '请输入正确的命令,多个命令空格分割', custom: '自定义', containerTerminal: '终端', emptyUser: '为空时,将使用容器默认的用户登录', + upgrade: '升级', + upgradeHelper: '该操作仅支持容器版本升级', + upgradeWarning: '当前目标版本低于原镜像版本,请重新输入!', + upgradeWarning2: '升级操作需要重建容器,任何未持久化的数据将会丢失,是否继续?', + oldImage: '当前镜像', + targetImage: '目标镜像', + appHelper: '该容器来源于应用商店,升级可能导致该服务不可用,是否继续?', + port: '端口', server: '服务器', serverExample: '例: 80, 80-88, ip:80 或者 ip:80-88', diff --git a/frontend/src/views/container/container/index.vue b/frontend/src/views/container/container/index.vue index e4c3340e9..2ddc9b7bf 100644 --- a/frontend/src/views/container/container/index.vue +++ b/frontend/src/views/container/container/index.vue @@ -16,25 +16,25 @@ {{ $t('container.containerPrune') }} - + {{ $t('container.start') }} - + {{ $t('container.stop') }} - + {{ $t('container.restart') }} - + {{ $t('container.kill') }} - + {{ $t('container.pause') }} - + {{ $t('container.unpause') }} - + {{ $t('container.remove') }} @@ -110,7 +110,7 @@ /> - + + @@ -133,7 +134,8 @@ import Tooltip from '@/components/tooltip/index.vue'; import TableSetting from '@/components/table-setting/index.vue'; import ReNameDialog from '@/views/container/container/rename/index.vue'; -import CreateDialog from '@/views/container/container/operate/index.vue'; +import OperateDialog from '@/views/container/container/operate/index.vue'; +import UpgraeDialog from '@/views/container/container/upgrade/index.vue'; import MonitorDialog from '@/views/container/container/monitor/index.vue'; import ContainerLogDialog from '@/views/container/container/log/index.vue'; import TerminalDialog from '@/views/container/container/terminal/index.vue'; @@ -164,6 +166,7 @@ const paginationConfig = reactive({ total: 0, }); const searchName = ref(); +const dialogUpgradeRef = ref(); const dockerStatus = ref('Running'); const loadStatus = async () => { @@ -231,11 +234,10 @@ const onOpenDialog = async ( cmd: [], cmdStr: '', exposedPorts: [], + cpuShares: 1024, nanoCPUs: 0, memory: 0, memoryItem: 0, - memoryUnit: 'MB', - cpuUnit: 'Core', volumes: [], labels: [], env: [], @@ -297,34 +299,35 @@ const onClean = () => { }); }; -const checkStatus = (operation: string) => { - if (selects.value.length < 1) { +const checkStatus = (operation: string, row: Container.ContainerInfo | null) => { + let opList = row ? [row] : selects.value; + if (opList.length < 1) { return true; } switch (operation) { case 'start': - for (const item of selects.value) { + for (const item of opList) { if (item.state === 'running') { return true; } } return false; case 'stop': - for (const item of selects.value) { + for (const item of opList) { if (item.state === 'stopped' || item.state === 'exited') { return true; } } return false; case 'pause': - for (const item of selects.value) { + for (const item of opList) { if (item.state === 'paused' || item.state === 'exited') { return true; } } return false; case 'unpause': - for (const item of selects.value) { + for (const item of opList) { if (item.state !== 'paused') { return true; } @@ -332,9 +335,10 @@ const checkStatus = (operation: string) => { return false; } }; -const onOperate = async (operation: string) => { +const onOperate = async (operation: string, row: Container.ContainerInfo | null) => { + let opList = row ? [row] : selects.value; let msg = i18n.global.t('container.operatorHelper', [i18n.global.t('container.' + operation)]); - for (const item of selects.value) { + for (const item of opList) { if (item.isFromApp) { msg = i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + operation)]); break; @@ -346,7 +350,7 @@ const onOperate = async (operation: string) => { type: 'info', }).then(() => { let ps = []; - for (const item of selects.value) { + for (const item of opList) { const param = { name: item.name, operation: operation, @@ -384,6 +388,12 @@ const buttons = [ onTerminal(row); }, }, + { + label: i18n.global.t('commons.button.log'), + click: (row: Container.ContainerInfo) => { + dialogContainerLogRef.value!.acceptParams({ containerID: row.containerID, container: row.name }); + }, + }, { label: i18n.global.t('container.monitor'), disabled: (row: Container.ContainerInfo) => { @@ -393,6 +403,12 @@ const buttons = [ onMonitor(row); }, }, + { + label: i18n.global.t('container.upgrade'), + click: (row: Container.ContainerInfo) => { + dialogUpgradeRef.value!.acceptParams({ container: row.name, image: row.imageName, fromApp: row.isFromApp }); + }, + }, { label: i18n.global.t('container.rename'), click: (row: Container.ContainerInfo) => { @@ -403,9 +419,66 @@ const buttons = [ }, }, { - label: i18n.global.t('commons.button.log'), + label: i18n.global.t('container.start'), click: (row: Container.ContainerInfo) => { - dialogContainerLogRef.value!.acceptParams({ containerID: row.containerID, container: row.name }); + onOperate('start', row); + }, + disabled: (row: any) => { + return checkStatus('start', row); + }, + }, + { + label: i18n.global.t('container.stop'), + click: (row: Container.ContainerInfo) => { + onOperate('stop', row); + }, + disabled: (row: any) => { + return checkStatus('stop', row); + }, + }, + { + label: i18n.global.t('container.restart'), + click: (row: Container.ContainerInfo) => { + onOperate('restart', row); + }, + disabled: (row: any) => { + return checkStatus('restart', row); + }, + }, + { + label: i18n.global.t('container.kill'), + click: (row: Container.ContainerInfo) => { + onOperate('kill', row); + }, + disabled: (row: any) => { + return checkStatus('kill', row); + }, + }, + { + label: i18n.global.t('container.pause'), + click: (row: Container.ContainerInfo) => { + onOperate('pause', row); + }, + disabled: (row: any) => { + return checkStatus('pause', row); + }, + }, + { + label: i18n.global.t('container.unpause'), + click: (row: Container.ContainerInfo) => { + onOperate('unpause', row); + }, + disabled: (row: any) => { + return checkStatus('unpause', row); + }, + }, + { + label: i18n.global.t('container.remove'), + click: (row: Container.ContainerInfo) => { + onOperate('remove', row); + }, + disabled: (row: any) => { + return checkStatus('remove', row); }, }, ]; diff --git a/frontend/src/views/container/container/operate/index.vue b/frontend/src/views/container/container/operate/index.vue index cb7e4079d..1c43b3892 100644 --- a/frontend/src/views/container/container/operate/index.vue +++ b/frontend/src/views/container/container/operate/index.vue @@ -121,7 +121,7 @@ - + @@ -252,9 +252,12 @@ const acceptParams = (params: DialogProps): void => { dialogData.value.rowData.cmdStr = itemCmd ? itemCmd.substring(0, itemCmd.length - 1) : ''; dialogData.value.rowData.labelsStr = dialogData.value.rowData.labels.join('\n'); dialogData.value.rowData.envStr = dialogData.value.rowData.env.join('\n'); + dialogData.value.rowData.exposedPorts = dialogData.value.rowData.exposedPorts || []; for (const item of dialogData.value.rowData.exposedPorts) { item.host = item.hostPort; } + dialogData.value.rowData.volumes = dialogData.value.rowData.volumes || []; + console.log(dialogData.value.rowData.cpuShares); } loadLimit(); loadImageOptions(); @@ -337,20 +340,18 @@ const onSubmit = async (formEl: FormInstance | undefined) => { if (!formEl) return; formEl.validate(async (valid) => { if (!valid) return; - if (dialogData.value.rowData!.envStr.length !== 0) { - dialogData.value.rowData!.env = dialogData.value.rowData!.envStr.split('\n'); + if (dialogData.value.rowData?.envStr) { + dialogData.value.rowData.env = dialogData.value.rowData!.envStr.split('\n'); } - if (dialogData.value.rowData!.labelsStr.length !== 0) { + if (dialogData.value.rowData?.labelsStr) { dialogData.value.rowData!.labels = dialogData.value.rowData!.labelsStr.split('\n'); } - if (dialogData.value.rowData!.cmdStr.length !== 0) { - let itemCmd = dialogData.value.rowData!.cmdStr.split(' '); + dialogData.value.rowData!.cmd = []; + if (dialogData.value.rowData?.cmdStr) { + let itemCmd = dialogData.value.rowData!.cmdStr.split(`'`); for (const cmd of itemCmd) { - if (cmd.startsWith(`'`) && cmd.endsWith(`'`) && cmd.length >= 3) { - dialogData.value.rowData!.cmd.push(cmd.substring(1, cmd.length - 2)); - } else { - MsgError(i18n.global.t('container.commandHelper')); - return; + if (cmd && cmd !== ' ') { + dialogData.value.rowData!.cmd.push(cmd); } } } diff --git a/frontend/src/views/container/container/upgrade/index.vue b/frontend/src/views/container/container/upgrade/index.vue new file mode 100644 index 000000000..584b4c677 --- /dev/null +++ b/frontend/src/views/container/container/upgrade/index.vue @@ -0,0 +1,141 @@ + + +