feat: 容器支持自定义控制台交互方式 (#2434)

This commit is contained in:
ssongliu 2023-10-07 14:30:46 +08:00 committed by GitHub
parent dc86cbc1ee
commit bca9777a43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 77 additions and 22 deletions

View File

@ -222,13 +222,13 @@ func (b *BaseApi) ContainerInfo(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Summary Load container limis
// @Summary Load container limits
// @Description 获取容器限制
// @Success 200 {object} dto.ResourceLimit
// @Security ApiKeyAuth
// @Router /containers/limit [get]
func (b *BaseApi) LoadResouceLimit(c *gin.Context) {
data, err := containerService.LoadResouceLimit()
func (b *BaseApi) LoadResourceLimit(c *gin.Context) {
data, err := containerService.LoadResourceLimit()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return

View File

@ -44,6 +44,8 @@ type ContainerOperate struct {
Network string `json:"network"`
PublishAllPorts bool `json:"publishAllPorts"`
ExposedPorts []PortHelper `json:"exposedPorts"`
Tty bool `json:"tty"`
OpenStdin bool `json:"openStdin"`
Cmd []string `json:"cmd"`
Entrypoint []string `json:"entrypoint"`
CPUShares int64 `json:"cpuShares"`
@ -70,7 +72,7 @@ type ContainerListStats struct {
CPUPercent float64 `json:"cpuPercent"`
PercpuUsage int `json:"percpuUsage"`
MemroyCache uint64 `json:"memoryCache"`
MemoryCache uint64 `json:"memoryCache"`
MemoryUsage uint64 `json:"memoryUsage"`
MemoryLimit uint64 `json:"memoryLimit"`
MemoryPercent float64 `json:"memoryPercent"`

View File

@ -51,7 +51,7 @@ type IContainerService interface {
ContainerUpgrade(req dto.ContainerUpgrade) error
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
ContainerListStats() ([]dto.ContainerListStats, error)
LoadResouceLimit() (*dto.ResourceLimit, error)
LoadResourceLimit() (*dto.ResourceLimit, error)
ContainerLogClean(req dto.OperationWithName) error
ContainerOperation(req dto.ContainerOperation) error
ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error
@ -288,7 +288,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
return report, nil
}
func (u *ContainerService) LoadResouceLimit() (*dto.ResourceLimit, error) {
func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
cpuCounts, err := cpu.Counts(true)
if err != nil {
return nil, fmt.Errorf("load cpu limit failed, err: %v", err)
@ -368,6 +368,8 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
data.Cmd = oldContainer.Config.Cmd
data.OpenStdin = oldContainer.Config.OpenStdin
data.Tty = oldContainer.Config.Tty
data.Entrypoint = oldContainer.Config.Entrypoint
data.Env = oldContainer.Config.Env
data.CPUShares = oldContainer.HostConfig.CPUShares
@ -441,7 +443,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
container, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
if err != nil {
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("update contianer failed, err: %v", err)
return fmt.Errorf("update container 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 {
@ -487,7 +489,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
container, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
if err != nil {
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("upgrade contianer failed, err: %v", err)
return fmt.Errorf("upgrade container failed, err: %v", err)
}
global.LOG.Infof("upgrade container %s successful! now check if the container is started.", req.Name)
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
@ -683,14 +685,14 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
}
func stringsToMap(list []string) map[string]string {
var lableMap = make(map[string]string)
var labelMap = make(map[string]string)
for _, label := range list {
if strings.Contains(label, "=") {
sps := strings.SplitN(label, "=", 2)
lableMap[sps[0]] = sps[1]
labelMap[sps[0]] = sps[1]
}
}
return lableMap
return labelMap
}
func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
@ -791,7 +793,7 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
data.CPUPercent = calculateCPUPercentUnix(stats)
data.PercpuUsage = len(stats.CPUStats.CPUUsage.PercpuUsage)
data.MemroyCache = stats.MemoryStats.Stats["cache"]
data.MemoryCache = stats.MemoryStats.Stats["cache"]
data.MemoryUsage = stats.MemoryStats.Usage
data.MemoryLimit = stats.MemoryStats.Limit
data.MemoryPercent = calculateMemPercentUnix(stats.MemoryStats)
@ -849,16 +851,18 @@ func loadConfigInfo(req dto.ContainerOperate, config *container.Config, hostConf
if err != nil {
return err
}
exposeds := make(nat.PortSet)
exposed := make(nat.PortSet)
for port := range portMap {
exposeds[port] = struct{}{}
exposed[port] = struct{}{}
}
config.Image = req.Image
config.Cmd = req.Cmd
config.Entrypoint = req.Entrypoint
config.Env = req.Env
config.Labels = stringsToMap(req.Labels)
config.ExposedPorts = exposeds
config.ExposedPorts = exposed
config.OpenStdin = req.OpenStdin
config.Tty = req.Tty
if len(req.Network) != 0 {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}

View File

@ -26,7 +26,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.POST("/list", baseApi.ListContainer)
baRouter.GET("/list/stats", baseApi.ContainerListStats)
baRouter.GET("/search/log", baseApi.ContainerLogs)
baRouter.GET("/limit", baseApi.LoadResouceLimit)
baRouter.GET("/limit", baseApi.LoadResourceLimit)
baRouter.POST("/clean/log", baseApi.CleanContainerLog)
baRouter.POST("/load/log", baseApi.LoadContainerLog)
baRouter.POST("/inspect", baseApi.Inspect)

View File

@ -1233,7 +1233,7 @@ const docTemplate = `{
}
},
"/containers/compose/search/log": {
"post": {
"get": {
"security": [
{
"ApiKeyAuth": []
@ -2037,7 +2037,7 @@ const docTemplate = `{
}
],
"description": "获取容器限制",
"summary": "Load container limis",
"summary": "Load container limits",
"responses": {
"200": {
"description": "OK",
@ -12549,12 +12549,18 @@ const docTemplate = `{
"network": {
"type": "string"
},
"openStdin": {
"type": "boolean"
},
"publishAllPorts": {
"type": "boolean"
},
"restartPolicy": {
"type": "string"
},
"tty": {
"type": "boolean"
},
"volumes": {
"type": "array",
"items": {
@ -16649,6 +16655,9 @@ const docTemplate = `{
"type": "object",
"additionalProperties": true
},
"port": {
"type": "integer"
},
"resource": {
"type": "string"
},
@ -16734,6 +16743,9 @@ const docTemplate = `{
"type": "object",
"additionalProperties": true
},
"port": {
"type": "integer"
},
"rebuild": {
"type": "boolean"
},

View File

@ -1226,7 +1226,7 @@
}
},
"/containers/compose/search/log": {
"post": {
"get": {
"security": [
{
"ApiKeyAuth": []
@ -2030,7 +2030,7 @@
}
],
"description": "获取容器限制",
"summary": "Load container limis",
"summary": "Load container limits",
"responses": {
"200": {
"description": "OK",
@ -12542,12 +12542,18 @@
"network": {
"type": "string"
},
"openStdin": {
"type": "boolean"
},
"publishAllPorts": {
"type": "boolean"
},
"restartPolicy": {
"type": "string"
},
"tty": {
"type": "boolean"
},
"volumes": {
"type": "array",
"items": {
@ -16642,6 +16648,9 @@
"type": "object",
"additionalProperties": true
},
"port": {
"type": "integer"
},
"resource": {
"type": "string"
},
@ -16727,6 +16736,9 @@
"type": "object",
"additionalProperties": true
},
"port": {
"type": "integer"
},
"rebuild": {
"type": "boolean"
},

View File

@ -403,10 +403,14 @@ definitions:
type: number
network:
type: string
openStdin:
type: boolean
publishAllPorts:
type: boolean
restartPolicy:
type: string
tty:
type: boolean
volumes:
items:
$ref: '#/definitions/dto.VolumeHelper'
@ -3152,6 +3156,8 @@ definitions:
params:
additionalProperties: true
type: object
port:
type: integer
resource:
type: string
source:
@ -3208,6 +3214,8 @@ definitions:
params:
additionalProperties: true
type: object
port:
type: integer
rebuild:
type: boolean
source:
@ -4909,7 +4917,7 @@ paths:
tags:
- Container Compose
/containers/compose/search/log:
post:
get:
description: docker-compose 日志
parameters:
- description: compose 文件地址
@ -5426,7 +5434,7 @@ paths:
$ref: '#/definitions/dto.ResourceLimit'
security:
- ApiKeyAuth: []
summary: Load container limis
summary: Load container limits
/containers/list:
post:
consumes:

View File

@ -27,6 +27,8 @@ export namespace Container {
entrypointStr: string;
memoryItem: number;
cmd: Array<string>;
openStdin: boolean;
tty: boolean;
entrypoint: Array<string>;
publishAllPorts: boolean;
exposedPorts: Array<Port>;

View File

@ -544,6 +544,9 @@ const message = {
'The default CPU share for a container is 1024, which can be increased to give the container more CPU time.',
command: 'Command',
console: 'Console Interaction',
tty: 'TTY (-t)',
openStdin: 'OpenStdin (-i)',
custom: 'Custom',
emptyUser: 'When empty, you will log in as default',
containerTerminal: 'Terminal',

View File

@ -529,6 +529,9 @@ const message = {
cpuShareHelper: '容器默認份額為 1024 CPU增大可使當前容器獲得更多的 CPU 時間',
command: '命令',
console: '控製臺交互',
tty: '偽終端 ( -t )',
openStdin: '標準輸入 ( -i )',
custom: '自定義',
containerTerminal: '終端',
emptyUser: '為空時將使用容器默認的用戶登錄',

View File

@ -529,6 +529,9 @@ const message = {
cpuShareHelper: '容器默认份额为 1024 CPU增大可使当前容器获得更多的 CPU 时间',
command: '命令',
console: '控制台交互',
tty: '伪终端 ( -t )',
openStdin: '标准输入 ( -i )',
custom: '自定义',
containerTerminal: '终端',
emptyUser: '为空时将使用容器默认的用户登录',

View File

@ -176,6 +176,12 @@
{{ $t('container.autoRemove') }}
</el-checkbox>
</el-form-item>
<el-form-item :label="$t('container.console')">
<el-checkbox v-model="dialogData.rowData!.tty">{{ $t('container.tty') }}</el-checkbox>
<el-checkbox v-model="dialogData.rowData!.openStdin">
{{ $t('container.openStdin') }}
</el-checkbox>
</el-form-item>
<el-form-item :label="$t('container.restartPolicy')" prop="restartPolicy">
<el-radio-group v-model="dialogData.rowData!.restartPolicy">
<el-radio label="no">{{ $t('container.no') }}</el-radio>