mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 02:59:16 +08:00
feat: 增加容器编辑功能 (#1381)
This commit is contained in:
parent
b7cda1d2f1
commit
352978b54d
@ -153,17 +153,69 @@ func (b *BaseApi) OperatorCompose(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Update container
|
||||
// @Description 更新容器
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新容器 [name][image]","formatEN":"update container [name][image]"}
|
||||
func (b *BaseApi) ContainerUpdate(c *gin.Context) {
|
||||
var req dto.ContainerOperate
|
||||
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.ContainerUpdate(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Load container info
|
||||
// @Description 获取容器表单信息
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Success 200 {object} dto.ContainerOperate
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/info [post]
|
||||
func (b *BaseApi) ContainerInfo(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
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
|
||||
}
|
||||
data, err := containerService.ContainerInfo(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Create container
|
||||
// @Description 创建容器
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerCreate true "request"
|
||||
// @Param request body dto.ContainerOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers [post]
|
||||
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器 [name][image]","formatEN":"create container [name][image]"}
|
||||
func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||
var req dto.ContainerCreate
|
||||
var req dto.ContainerOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
@ -30,7 +30,7 @@ type ContainerInfo struct {
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
}
|
||||
|
||||
type ContainerCreate struct {
|
||||
type ContainerOperate struct {
|
||||
Name string `json:"name"`
|
||||
Image string `json:"image"`
|
||||
PublishAllPorts bool `json:"publishAllPorts"`
|
||||
|
@ -39,7 +39,9 @@ type IContainerService interface {
|
||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerCreate) error
|
||||
ContainerCreate(req dto.ContainerOperate) error
|
||||
ContainerUpdate(req dto.ContainerOperate) error
|
||||
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
|
||||
ContainerLogClean(req dto.OperationWithName) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error
|
||||
@ -213,55 +215,16 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exposeds := make(nat.PortSet)
|
||||
for port := range portMap {
|
||||
exposeds[port] = struct{}{}
|
||||
}
|
||||
config := &container.Config{
|
||||
Image: req.Image,
|
||||
Cmd: req.Cmd,
|
||||
Env: req.Env,
|
||||
Labels: stringsToMap(req.Labels),
|
||||
Tty: true,
|
||||
OpenStdin: true,
|
||||
ExposedPorts: exposeds,
|
||||
}
|
||||
hostConf := &container.HostConfig{
|
||||
AutoRemove: req.AutoRemove,
|
||||
PublishAllPorts: req.PublishAllPorts,
|
||||
RestartPolicy: container.RestartPolicy{Name: req.RestartPolicy},
|
||||
}
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
hostConf.RestartPolicy.MaximumRetryCount = 5
|
||||
}
|
||||
if req.NanoCPUs != 0 {
|
||||
hostConf.NanoCPUs = req.NanoCPUs * 1000000000
|
||||
}
|
||||
if req.CPUShares != 0 {
|
||||
hostConf.CPUShares = req.CPUShares
|
||||
}
|
||||
if req.Memory != 0 {
|
||||
hostConf.Memory = req.Memory
|
||||
}
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
hostConf.PortBindings = portMap
|
||||
}
|
||||
if len(req.Volumes) != 0 {
|
||||
config.Volumes = make(map[string]struct{})
|
||||
for _, volume := range req.Volumes {
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
}
|
||||
var config *container.Config
|
||||
var hostConf *container.HostConfig
|
||||
if err := loadConfigInfo(req, config, hostConf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
@ -285,6 +248,95 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx := context.Background()
|
||||
oldContainer, err := client.ContainerInspect(ctx, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data dto.ContainerOperate
|
||||
data.Name = strings.ReplaceAll(oldContainer.Name, "/", "")
|
||||
data.Image = oldContainer.Config.Image
|
||||
data.Cmd = oldContainer.Config.Cmd
|
||||
data.Env = oldContainer.Config.Env
|
||||
for key, val := range oldContainer.Config.Labels {
|
||||
data.Labels = append(data.Labels, fmt.Sprintf("%s=%s", key, val))
|
||||
}
|
||||
for key, val := range oldContainer.HostConfig.PortBindings {
|
||||
var itemPort dto.PortHelper
|
||||
if !strings.Contains(string(key), "/") {
|
||||
continue
|
||||
}
|
||||
itemPort.ContainerPort = strings.Split(string(key), "/")[0]
|
||||
itemPort.Protocol = strings.Split(string(key), "/")[1]
|
||||
for _, binds := range val {
|
||||
itemPort.HostIP = binds.HostIP
|
||||
itemPort.HostPort = binds.HostPort
|
||||
data.ExposedPorts = append(data.ExposedPorts, itemPort)
|
||||
}
|
||||
}
|
||||
data.AutoRemove = oldContainer.HostConfig.AutoRemove
|
||||
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
|
||||
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
|
||||
if oldContainer.HostConfig.NanoCPUs != 0 {
|
||||
data.NanoCPUs = oldContainer.HostConfig.NanoCPUs / 1000000000
|
||||
}
|
||||
if oldContainer.HostConfig.Memory != 0 {
|
||||
data.Memory = oldContainer.HostConfig.Memory
|
||||
}
|
||||
for _, bind := range oldContainer.HostConfig.Binds {
|
||||
parts := strings.Split(bind, ":")
|
||||
if len(parts) != 3 {
|
||||
continue
|
||||
}
|
||||
data.Volumes = append(data.Volumes, dto.VolumeHelper{SourceDir: parts[0], ContainerDir: parts[1], Mode: parts[2]})
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) 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
|
||||
hostConf := oldContainer.HostConfig
|
||||
if err := loadConfigInfo(req, config, hostConf); err != nil {
|
||||
return err
|
||||
}
|
||||
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()
|
||||
@ -559,3 +611,44 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
}
|
||||
return portMap, nil
|
||||
}
|
||||
|
||||
func loadConfigInfo(req dto.ContainerOperate, config *container.Config, hostConf *container.HostConfig) error {
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exposeds := make(nat.PortSet)
|
||||
for port := range portMap {
|
||||
exposeds[port] = struct{}{}
|
||||
}
|
||||
config.Image = req.Image
|
||||
config.Cmd = req.Cmd
|
||||
config.Env = req.Env
|
||||
config.Labels = stringsToMap(req.Labels)
|
||||
config.ExposedPorts = exposeds
|
||||
|
||||
hostConf.AutoRemove = req.AutoRemove
|
||||
hostConf.PublishAllPorts = req.PublishAllPorts
|
||||
hostConf.RestartPolicy = container.RestartPolicy{Name: req.RestartPolicy}
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
hostConf.RestartPolicy.MaximumRetryCount = 5
|
||||
}
|
||||
if req.NanoCPUs != 0 {
|
||||
hostConf.NanoCPUs = req.NanoCPUs * 1000000000
|
||||
}
|
||||
if req.Memory != 0 {
|
||||
hostConf.Memory = req.Memory
|
||||
}
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
hostConf.PortBindings = portMap
|
||||
}
|
||||
hostConf.Binds = []string{}
|
||||
if len(req.Volumes) != 0 {
|
||||
config.Volumes = make(map[string]struct{})
|
||||
for _, volume := range req.Volumes {
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
||||
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
||||
|
||||
baRouter.POST("", baseApi.ContainerCreate)
|
||||
baRouter.POST("/update", baseApi.ContainerUpdate)
|
||||
baRouter.POST("/info", baseApi.ContainerInfo)
|
||||
baRouter.POST("/search", baseApi.SearchContainer)
|
||||
baRouter.GET("/search/log", baseApi.ContainerLogs)
|
||||
baRouter.POST("/clean/log", baseApi.CleanContainerLog)
|
||||
|
@ -916,7 +916,7 @@ var doc = `{
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ContainerCreate"
|
||||
"$ref": "#/definitions/dto.ContainerOperate"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -1781,6 +1781,42 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/info": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取容器表单信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Container"
|
||||
],
|
||||
"summary": "Load container info",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.OperationWithName"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ContainerOperate"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/inspect": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -2597,6 +2633,49 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新容器",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Container"
|
||||
],
|
||||
"summary": "Update container",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ContainerOperate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"name",
|
||||
"image"
|
||||
],
|
||||
"formatEN": "update container [name][image]",
|
||||
"formatZH": "更新容器 [name][image]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/volume": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -10584,7 +10663,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ContainerCreate": {
|
||||
"dto.ContainerOperate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"autoRemove": {
|
||||
@ -10596,6 +10675,9 @@ var doc = `{
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cpushares": {
|
||||
"type": "integer"
|
||||
},
|
||||
"env": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -902,7 +902,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ContainerCreate"
|
||||
"$ref": "#/definitions/dto.ContainerOperate"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -1767,6 +1767,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/info": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取容器表单信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Container"
|
||||
],
|
||||
"summary": "Load container info",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.OperationWithName"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ContainerOperate"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/inspect": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -2583,6 +2619,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新容器",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Container"
|
||||
],
|
||||
"summary": "Update container",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ContainerOperate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"name",
|
||||
"image"
|
||||
],
|
||||
"formatEN": "update container [name][image]",
|
||||
"formatZH": "更新容器 [name][image]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/volume": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -10570,7 +10649,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ContainerCreate": {
|
||||
"dto.ContainerOperate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"autoRemove": {
|
||||
@ -10582,6 +10661,9 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cpushares": {
|
||||
"type": "integer"
|
||||
},
|
||||
"env": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -253,7 +253,7 @@ definitions:
|
||||
- name
|
||||
- path
|
||||
type: object
|
||||
dto.ContainerCreate:
|
||||
dto.ContainerOperate:
|
||||
properties:
|
||||
autoRemove:
|
||||
type: boolean
|
||||
@ -261,6 +261,8 @@ definitions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
cpushares:
|
||||
type: integer
|
||||
env:
|
||||
items:
|
||||
type: string
|
||||
@ -3926,7 +3928,7 @@ paths:
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.ContainerCreate'
|
||||
$ref: '#/definitions/dto.ContainerOperate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
@ -4483,6 +4485,28 @@ paths:
|
||||
formatEN: tag image [reponame][targetName]
|
||||
formatZH: tag 镜像 [reponame][targetName]
|
||||
paramKeys: []
|
||||
/containers/info:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取容器表单信息
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.OperationWithName'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.ContainerOperate'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load container info
|
||||
tags:
|
||||
- Container
|
||||
/containers/inspect:
|
||||
post:
|
||||
consumes:
|
||||
@ -5000,6 +5024,34 @@ paths:
|
||||
formatEN: update compose template information [name]
|
||||
formatZH: 更新 compose 模版 [name]
|
||||
paramKeys: []
|
||||
/containers/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新容器
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.ContainerOperate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update container
|
||||
tags:
|
||||
- Container
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- name
|
||||
- image
|
||||
formatEN: update container [name][image]
|
||||
formatZH: 更新容器 [name][image]
|
||||
paramKeys: []
|
||||
/containers/volume:
|
||||
post:
|
||||
consumes:
|
||||
|
@ -10,13 +10,18 @@ export namespace Container {
|
||||
name: string;
|
||||
filters: string;
|
||||
}
|
||||
export interface ContainerCreate {
|
||||
export interface ContainerHelper {
|
||||
name: string;
|
||||
image: string;
|
||||
cmdStr: string;
|
||||
memoryUnit: string;
|
||||
memoryItem: number;
|
||||
cmd: Array<string>;
|
||||
publishAllPorts: boolean;
|
||||
exposedPorts: Array<Port>;
|
||||
nanoCPUs: number;
|
||||
cpuShares: number;
|
||||
cpuUnit: string;
|
||||
memory: number;
|
||||
volumes: Array<Volume>;
|
||||
autoRemove: boolean;
|
||||
|
@ -5,9 +5,15 @@ import { Container } from '../interface/container';
|
||||
export const searchContainer = (params: Container.ContainerSearch) => {
|
||||
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params, 400000);
|
||||
};
|
||||
export const createContainer = (params: Container.ContainerCreate) => {
|
||||
export const createContainer = (params: Container.ContainerHelper) => {
|
||||
return http.post(`/containers`, params, 3000000);
|
||||
};
|
||||
export const updateContainer = (params: Container.ContainerHelper) => {
|
||||
return http.post(`/containers/update`, params, 3000000);
|
||||
};
|
||||
export const loadContainerInfo = (name: string) => {
|
||||
return http.post<Container.ContainerHelper>(`/containers/info`, { name: name });
|
||||
};
|
||||
export const cleanContainerLog = (containerName: string) => {
|
||||
return http.post(`/containers/clean/log`, { name: containerName });
|
||||
};
|
||||
|
@ -444,6 +444,8 @@ const message = {
|
||||
},
|
||||
container: {
|
||||
createContainer: 'Create container',
|
||||
updateContaienrHelper:
|
||||
'Container editing requires rebuilding the container. Any data that has not been persisted will be lost. Do you want to continue?',
|
||||
containerList: 'Container list',
|
||||
operatorHelper: '{0} will be performed on the selected container. Do you want to continue?',
|
||||
operatorAppHelper:
|
||||
@ -489,6 +491,7 @@ 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',
|
||||
@ -514,9 +517,10 @@ const message = {
|
||||
mode: 'Mode',
|
||||
env: 'Environment',
|
||||
restartPolicy: 'Restart policy',
|
||||
always: 'always',
|
||||
unlessStopped: 'unless-stopped',
|
||||
onFailure: 'on-failure(five times by default)',
|
||||
no: 'no',
|
||||
no: 'never',
|
||||
|
||||
image: 'Image',
|
||||
imagePull: 'Image pull',
|
||||
|
@ -453,6 +453,7 @@ const message = {
|
||||
},
|
||||
container: {
|
||||
createContainer: '创建容器',
|
||||
updateContaienrHelper: '容器编辑需要重建容器,任何未持久化的数据将会丢失,是否继续?',
|
||||
containerList: '容器列表',
|
||||
operatorHelper: '将对选中容器进行 {0} 操作,是否继续?',
|
||||
operatorAppHelper: '存在来源于应用商店的容器,{0} 操作可能会影响到该服务的正常使用,是否确认?',
|
||||
@ -495,6 +496,7 @@ const message = {
|
||||
|
||||
user: '用户',
|
||||
command: '命令',
|
||||
commandHelper: '请输入正确的命令,多个命令空格分割',
|
||||
custom: '自定义',
|
||||
containerTerminal: '终端',
|
||||
emptyUser: '为空时,将使用容器默认的用户登录',
|
||||
@ -520,6 +522,7 @@ const message = {
|
||||
mode: '权限',
|
||||
env: '环境变量',
|
||||
restartPolicy: '重启规则',
|
||||
always: '一直重启',
|
||||
unlessStopped: '关闭后重启',
|
||||
onFailure: '失败后重启(默认重启 5 次)',
|
||||
no: '不重启',
|
||||
|
@ -9,7 +9,7 @@
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16">
|
||||
<el-button type="primary" @click="onCreate()">
|
||||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('container.createContainer') }}
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="onClean()">
|
||||
@ -123,7 +123,7 @@
|
||||
|
||||
<ReNameDialog @search="search" ref="dialogReNameRef" />
|
||||
<ContainerLogDialog ref="dialogContainerLogRef" />
|
||||
<CreateDialog @search="search" ref="dialogCreateRef" />
|
||||
<CreateDialog @search="search" ref="dialogOperateRef" />
|
||||
<MonitorDialog ref="dialogMonitorRef" />
|
||||
<TerminalDialog ref="dialogTerminalRef" />
|
||||
</div>
|
||||
@ -133,14 +133,21 @@
|
||||
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/create/index.vue';
|
||||
import CreateDialog from '@/views/container/container/operate/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';
|
||||
import CodemirrorDialog from '@/components/codemirror-dialog/index.vue';
|
||||
import Status from '@/components/status/index.vue';
|
||||
import { reactive, onMounted, ref } from 'vue';
|
||||
import { ContainerOperator, containerPrune, inspect, loadDockerStatus, searchContainer } from '@/api/modules/container';
|
||||
import {
|
||||
ContainerOperator,
|
||||
containerPrune,
|
||||
inspect,
|
||||
loadContainerInfo,
|
||||
loadDockerStatus,
|
||||
searchContainer,
|
||||
} from '@/api/modules/container';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
@ -211,9 +218,35 @@ const search = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const dialogCreateRef = ref();
|
||||
const onCreate = () => {
|
||||
dialogCreateRef.value!.acceptParams();
|
||||
const dialogOperateRef = ref();
|
||||
const onEdit = async (container: string) => {
|
||||
const res = await loadContainerInfo(container);
|
||||
if (res.data) {
|
||||
onOpenDialog('edit', res.data);
|
||||
}
|
||||
};
|
||||
const onOpenDialog = async (
|
||||
title: string,
|
||||
rowData: Partial<Container.ContainerHelper> = {
|
||||
cmd: [],
|
||||
cmdStr: '',
|
||||
exposedPorts: [],
|
||||
nanoCPUs: 0,
|
||||
memory: 0,
|
||||
memoryItem: 0,
|
||||
memoryUnit: 'MB',
|
||||
cpuUnit: 'Core',
|
||||
volumes: [],
|
||||
labels: [],
|
||||
env: [],
|
||||
restartPolicy: 'no',
|
||||
},
|
||||
) => {
|
||||
let params = {
|
||||
title,
|
||||
rowData: { ...rowData },
|
||||
};
|
||||
dialogOperateRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
const dialogMonitorRef = ref();
|
||||
@ -336,6 +369,12 @@ const onOperate = async (operation: string) => {
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
click: (row: Container.ContainerInfo) => {
|
||||
onEdit(row.containerID);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('file.terminal'),
|
||||
disabled: (row: Container.ContainerInfo) => {
|
||||
|
@ -3,14 +3,21 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.createContainer')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form ref="formRef" label-position="top" v-loading="loading" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="top"
|
||||
v-loading="loading"
|
||||
:model="dialogData.rowData!"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('container.name')" prop="name">
|
||||
<el-input clearable v-model.trim="form.name" />
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.name" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.image')" prop="image">
|
||||
<el-select class="widthClass" allow-create filterable v-model="form.image">
|
||||
<el-select class="widthClass" allow-create filterable v-model="dialogData.rowData!.image">
|
||||
<el-option
|
||||
v-for="(item, index) of images"
|
||||
:key="index"
|
||||
@ -20,15 +27,15 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.port')">
|
||||
<el-radio-group v-model="form.publishAllPorts" class="ml-4">
|
||||
<el-radio-group v-model="dialogData.rowData!.publishAllPorts" class="ml-4">
|
||||
<el-radio :label="false">{{ $t('container.exposePort') }}</el-radio>
|
||||
<el-radio :label="true">{{ $t('container.exposeAll') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!form.publishAllPorts">
|
||||
<el-form-item v-if="!dialogData.rowData!.publishAllPorts">
|
||||
<el-card class="widthClass">
|
||||
<table style="width: 100%" class="tab-table">
|
||||
<tr v-if="form.exposedPorts.length !== 0">
|
||||
<tr v-if="dialogData.rowData!.exposedPorts.length !== 0">
|
||||
<th scope="col" width="45%" align="left">
|
||||
<label>{{ $t('container.server') }}</label>
|
||||
</th>
|
||||
@ -40,7 +47,7 @@
|
||||
</th>
|
||||
<th align="left"></th>
|
||||
</tr>
|
||||
<tr v-for="(row, index) in form.exposedPorts" :key="index">
|
||||
<tr v-for="(row, index) in dialogData.rowData!.exposedPorts" :key="index">
|
||||
<td width="45%">
|
||||
<el-input
|
||||
:placeholder="$t('container.serverExample')"
|
||||
@ -76,19 +83,21 @@
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.cmd')" prop="cmdStr">
|
||||
<el-input :placeholder="$t('container.cmdHelper')" v-model="form.cmdStr" />
|
||||
<el-input :placeholder="$t('container.cmdHelper')" v-model="dialogData.rowData!.cmdStr" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="autoRemove">
|
||||
<el-checkbox v-model="form.autoRemove">{{ $t('container.autoRemove') }}</el-checkbox>
|
||||
<el-checkbox v-model="dialogData.rowData!.autoRemove">
|
||||
{{ $t('container.autoRemove') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.cpuShare')" prop="cpuShares">
|
||||
<el-input style="width: 40%" v-model.number="form.cpuShares" />
|
||||
<el-input style="width: 40%" v-model.number="dialogData.rowData!.cpuShares" />
|
||||
<span class="input-help">{{ $t('container.cpuShareHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.cpuQuota')" prop="nanoCPUs">
|
||||
<el-input type="number" style="width: 40%" v-model.number="form.nanoCPUs">
|
||||
<el-input type="number" style="width: 40%" v-model.number="dialogData.rowData!.nanoCPUs">
|
||||
<template #append>
|
||||
<el-select v-model="form.cpuUnit" disabled style="width: 85px">
|
||||
<el-select v-model="dialogData.rowData!.cpuUnit" disabled style="width: 85px">
|
||||
<el-option label="Core" value="Core" />
|
||||
</el-select>
|
||||
</template>
|
||||
@ -96,9 +105,13 @@
|
||||
<span class="input-help">{{ $t('container.limitHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.memoryLimit')" prop="memoryItem">
|
||||
<el-input style="width: 40%" v-model.number="form.memoryItem">
|
||||
<el-input style="width: 40%" v-model.number="dialogData.rowData!.memoryItem">
|
||||
<template #append>
|
||||
<el-select v-model="form.memoryUnit" placeholder="Select" style="width: 85px">
|
||||
<el-select
|
||||
v-model="dialogData.rowData!.memoryUnit"
|
||||
placeholder="Select"
|
||||
style="width: 85px"
|
||||
>
|
||||
<el-option label="KB" value="KB" />
|
||||
<el-option label="MB" value="MB" />
|
||||
<el-option label="GB" value="GB" />
|
||||
@ -110,7 +123,7 @@
|
||||
<el-form-item :label="$t('container.mount')">
|
||||
<el-card style="width: 100%">
|
||||
<table style="width: 100%" class="tab-table">
|
||||
<tr v-if="form.volumes.length !== 0">
|
||||
<tr v-if="dialogData.rowData!.volumes.length !== 0">
|
||||
<th scope="col" width="39%" align="left">
|
||||
<label>{{ $t('container.serverPath') }}</label>
|
||||
</th>
|
||||
@ -122,7 +135,7 @@
|
||||
</th>
|
||||
<th align="left"></th>
|
||||
</tr>
|
||||
<tr v-for="(row, index) in form.volumes" :key="index">
|
||||
<tr v-for="(row, index) in dialogData.rowData!.volumes" :key="index">
|
||||
<td width="39%">
|
||||
<el-select
|
||||
class="widthClass"
|
||||
@ -169,23 +182,24 @@
|
||||
<el-input
|
||||
type="textarea"
|
||||
:placeholder="$t('container.tagHelper')"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
v-model="form.labelsStr"
|
||||
:autosize="{ minRows: 2, maxRows: 10 }"
|
||||
v-model="dialogData.rowData!.labelsStr"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.env')" prop="envStr">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:placeholder="$t('container.tagHelper')"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
v-model="form.envStr"
|
||||
:autosize="{ minRows: 2, maxRows: 10 }"
|
||||
v-model="dialogData.rowData!.envStr"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.restartPolicy')" prop="restartPolicy">
|
||||
<el-radio-group v-model="form.restartPolicy">
|
||||
<el-radio label="unless-stopped">{{ $t('container.unlessStopped') }}</el-radio>
|
||||
<el-radio label="on-failure">{{ $t('container.onFailure') }}</el-radio>
|
||||
<el-radio-group v-model="dialogData.rowData!.restartPolicy">
|
||||
<el-radio label="no">{{ $t('container.no') }}</el-radio>
|
||||
<el-radio label="always">{{ $t('container.always') }}</el-radio>
|
||||
<el-radio label="on-failure">{{ $t('container.onFailure') }}</el-radio>
|
||||
<el-radio label="unless-stopped">{{ $t('container.unlessStopped') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -210,73 +224,56 @@ import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { listImage, listVolume, createContainer } from '@/api/modules/container';
|
||||
import { listImage, listVolume, createContainer, updateContainer } from '@/api/modules/container';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { checkIp, checkPort } from '@/utils/util';
|
||||
import { checkIp, checkPort, computeSize } from '@/utils/util';
|
||||
|
||||
const loading = ref(false);
|
||||
interface DialogProps {
|
||||
title: string;
|
||||
rowData?: Container.ContainerHelper;
|
||||
getTableList?: () => Promise<any>;
|
||||
}
|
||||
|
||||
const title = ref<string>('');
|
||||
const drawerVisiable = ref(false);
|
||||
const form = reactive({
|
||||
name: '',
|
||||
image: '',
|
||||
cmdStr: '',
|
||||
cmd: [] as Array<string>,
|
||||
publishAllPorts: false,
|
||||
exposedPorts: [] as Array<Container.Port>,
|
||||
cpuShares: 1024,
|
||||
nanoCPUs: 0,
|
||||
memory: 0,
|
||||
memoryItem: 0,
|
||||
memoryUnit: 'MB',
|
||||
cpuUnit: 'Core',
|
||||
volumes: [] as Array<Container.Volume>,
|
||||
autoRemove: false,
|
||||
labels: [] as Array<string>,
|
||||
labelsStr: '',
|
||||
env: [] as Array<string>,
|
||||
envStr: '',
|
||||
restartPolicy: '',
|
||||
});
|
||||
const images = ref();
|
||||
const volumes = ref();
|
||||
|
||||
const acceptParams = (): void => {
|
||||
handlReset();
|
||||
drawerVisiable.value = true;
|
||||
const dialogData = ref<DialogProps>({
|
||||
title: '',
|
||||
});
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
dialogData.value = params;
|
||||
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
|
||||
if (params.title === 'edit') {
|
||||
dialogData.value.rowData.cpuUnit = 'Core';
|
||||
let itemMem = computeSize(Number(dialogData.value.rowData.memory));
|
||||
dialogData.value.rowData.memoryItem = itemMem.indexOf(' ') !== -1 ? Number(itemMem.split(' ')[0]) : 0;
|
||||
dialogData.value.rowData.memoryUnit = itemMem.indexOf(' ') !== -1 ? itemMem.split(' ')[1] : 'MB';
|
||||
let itemCmd = '';
|
||||
for (const item of dialogData.value.rowData.cmd) {
|
||||
itemCmd += `'${item}' `;
|
||||
}
|
||||
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');
|
||||
for (const item of dialogData.value.rowData.exposedPorts) {
|
||||
item.host = item.hostPort;
|
||||
}
|
||||
}
|
||||
loadImageOptions();
|
||||
loadVolumeOptions();
|
||||
drawerVisiable.value = true;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const handlReset = () => {
|
||||
form.name = '';
|
||||
form.image = '';
|
||||
form.cmdStr = '';
|
||||
form.cmd = [];
|
||||
form.publishAllPorts = false;
|
||||
form.exposedPorts = [];
|
||||
form.cpuShares = 1024;
|
||||
form.nanoCPUs = 0;
|
||||
form.memory = 0;
|
||||
form.memoryItem = 0;
|
||||
form.memoryUnit = 'MB';
|
||||
form.cpuUnit = 'Core';
|
||||
form.volumes = [];
|
||||
form.autoRemove = false;
|
||||
form.labels = [];
|
||||
form.labelsStr = '';
|
||||
form.env = [];
|
||||
form.envStr = '';
|
||||
form.restartPolicy = 'no';
|
||||
};
|
||||
const images = ref();
|
||||
const volumes = ref();
|
||||
|
||||
const handleClose = () => {
|
||||
drawerVisiable.value = false;
|
||||
};
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const rules = reactive({
|
||||
cpuShares: [Rules.number, checkNumberRange(2, 262144)],
|
||||
name: [Rules.requiredInput, Rules.name],
|
||||
@ -296,10 +293,10 @@ const handlePortsAdd = () => {
|
||||
hostPort: '',
|
||||
protocol: 'tcp',
|
||||
};
|
||||
form.exposedPorts.push(item);
|
||||
dialogData.value.rowData!.exposedPorts.push(item);
|
||||
};
|
||||
const handlePortsDelete = (index: number) => {
|
||||
form.exposedPorts.splice(index, 1);
|
||||
dialogData.value.rowData!.exposedPorts.splice(index, 1);
|
||||
};
|
||||
|
||||
const handleVolumesAdd = () => {
|
||||
@ -308,10 +305,10 @@ const handleVolumesAdd = () => {
|
||||
containerDir: '',
|
||||
mode: 'rw',
|
||||
};
|
||||
form.volumes.push(item);
|
||||
dialogData.value.rowData!.volumes.push(item);
|
||||
};
|
||||
const handleVolumesDelete = (index: number) => {
|
||||
form.volumes.splice(index, 1);
|
||||
dialogData.value.rowData!.volumes.splice(index, 1);
|
||||
};
|
||||
|
||||
const loadImageOptions = async () => {
|
||||
@ -323,8 +320,8 @@ const loadVolumeOptions = async () => {
|
||||
volumes.value = res.data;
|
||||
};
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (form.volumes.length !== 0) {
|
||||
for (const item of form.volumes) {
|
||||
if (dialogData.value.rowData!.volumes.length !== 0) {
|
||||
for (const item of dialogData.value.rowData!.volumes) {
|
||||
if (!item.containerDir || !item.sourceDir) {
|
||||
MsgError(i18n.global.t('container.volumeHelper'));
|
||||
return;
|
||||
@ -334,31 +331,59 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
if (form.envStr.length !== 0) {
|
||||
form.env = form.envStr.split('\n');
|
||||
if (dialogData.value.rowData!.envStr.length !== 0) {
|
||||
dialogData.value.rowData!.env = dialogData.value.rowData!.envStr.split('\n');
|
||||
}
|
||||
if (dialogData.value.rowData!.labelsStr.length !== 0) {
|
||||
dialogData.value.rowData!.labels = dialogData.value.rowData!.labelsStr.split('\n');
|
||||
}
|
||||
if (dialogData.value.rowData!.cmdStr.length !== 0) {
|
||||
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 (form.labelsStr.length !== 0) {
|
||||
form.labels = form.labelsStr.split('\n');
|
||||
}
|
||||
if (form.cmdStr.length !== 0) {
|
||||
form.cmd = form.cmdStr.split(' ');
|
||||
}
|
||||
if (!checkPortValid()) {
|
||||
return;
|
||||
}
|
||||
switch (form.memoryUnit) {
|
||||
switch (dialogData.value.rowData!.memoryUnit) {
|
||||
case 'KB':
|
||||
form.memory = form.memoryItem * 1024;
|
||||
dialogData.value.rowData!.memory = dialogData.value.rowData!.memoryItem * 1024;
|
||||
break;
|
||||
case 'MB':
|
||||
form.memory = form.memoryItem * 1024 * 1024;
|
||||
dialogData.value.rowData!.memory = dialogData.value.rowData!.memoryItem * 1024 * 1024;
|
||||
break;
|
||||
case 'GB':
|
||||
form.memory = form.memoryItem * 1024 * 1024 * 1024;
|
||||
dialogData.value.rowData!.memory = dialogData.value.rowData!.memoryItem * 1024 * 1024 * 1024;
|
||||
break;
|
||||
}
|
||||
loading.value = true;
|
||||
await createContainer(form)
|
||||
if (dialogData.value.title === 'create') {
|
||||
await createContainer(dialogData.value.rowData!)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
emit('search');
|
||||
drawerVisiable.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
} else {
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('container.updateContaienrHelper'),
|
||||
i18n.global.t('commons.button.edit'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
},
|
||||
).then(async () => {
|
||||
await updateContainer(dialogData.value.rowData!)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
@ -369,13 +394,15 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const checkPortValid = () => {
|
||||
if (form.exposedPorts.length === 0) {
|
||||
if (dialogData.value.rowData!.exposedPorts.length === 0) {
|
||||
return true;
|
||||
}
|
||||
for (const port of form.exposedPorts) {
|
||||
for (const port of dialogData.value.rowData!.exposedPorts) {
|
||||
if (port.host.indexOf(':') !== -1) {
|
||||
port.hostIP = port.host.split(':')[0];
|
||||
if (checkIp(port.hostIP)) {
|
Loading…
Reference in New Issue
Block a user