diff --git a/agent/app/dto/container.go b/agent/app/dto/container.go index 28e2cdac1..f2851f058 100644 --- a/agent/app/dto/container.go +++ b/agent/app/dto/container.go @@ -71,12 +71,18 @@ type ContainerOperate struct { Name string `json:"name" validate:"required"` Image string `json:"image" validate:"required"` Network string `json:"network"` + Hostname string `json:"hostname"` + DomainName string `json:"domainName"` + MacAddr string `json:"macAddr"` + DNS []string `json:"dns"` Ipv4 string `json:"ipv4"` Ipv6 string `json:"ipv6"` PublishAllPorts bool `json:"publishAllPorts"` ExposedPorts []PortHelper `json:"exposedPorts"` Tty bool `json:"tty"` OpenStdin bool `json:"openStdin"` + WorkingDir string `json:"workingDir"` + User string `json:"user"` Cmd []string `json:"cmd"` Entrypoint []string `json:"entrypoint"` CPUShares int64 `json:"cpuShares"` diff --git a/agent/app/service/container.go b/agent/app/service/container.go index fc1f61a52..35730354d 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -557,14 +557,20 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai bridgeNetworkSettings := networkSettings.Networks[data.Network] if bridgeNetworkSettings.IPAMConfig != nil { ipv4Address := bridgeNetworkSettings.IPAMConfig.IPv4Address + data.MacAddr = bridgeNetworkSettings.MacAddress data.Ipv4 = ipv4Address ipv6Address := bridgeNetworkSettings.IPAMConfig.IPv6Address data.Ipv6 = ipv6Address } else { data.Ipv4 = bridgeNetworkSettings.IPAddress } + data.Hostname = oldContainer.Config.Hostname + data.DNS = oldContainer.HostConfig.DNS + data.DomainName = oldContainer.Config.Domainname data.Cmd = oldContainer.Config.Cmd + data.WorkingDir = oldContainer.Config.WorkingDir + data.User = oldContainer.Config.User data.OpenStdin = oldContainer.Config.OpenStdin data.Tty = oldContainer.Config.Tty data.Entrypoint = oldContainer.Config.Entrypoint @@ -973,7 +979,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error return nil, err } res.Body.Close() - var stats *types.StatsJSON + var stats *container.StatsResponse if err := json.Unmarshal(body, &stats); err != nil { return nil, err } @@ -1037,7 +1043,7 @@ func stringsToMap(list []string) map[string]string { return labelMap } -func calculateCPUPercentUnix(stats *types.StatsJSON) float64 { +func calculateCPUPercentUnix(stats *container.StatsResponse) float64 { cpuPercent := 0.0 cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(stats.PreCPUStats.CPUUsage.TotalUsage) systemDelta := float64(stats.CPUStats.SystemUsage) - float64(stats.PreCPUStats.SystemUsage) @@ -1050,7 +1056,7 @@ func calculateCPUPercentUnix(stats *types.StatsJSON) float64 { } return cpuPercent } -func calculateMemPercentUnix(memStats types.MemoryStats) float64 { +func calculateMemPercentUnix(memStats container.MemoryStats) float64 { memPercent := 0.0 memUsage := float64(memStats.Usage) memLimit := float64(memStats.Limit) @@ -1059,7 +1065,7 @@ func calculateMemPercentUnix(memStats types.MemoryStats) float64 { } return memPercent } -func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) { +func calculateBlockIO(blkio container.BlkioStats) (blkRead float64, blkWrite float64) { for _, bioEntry := range blkio.IoServiceBytesRecursive { switch strings.ToLower(bioEntry.Op) { case "read": @@ -1070,7 +1076,7 @@ func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64 } return } -func calculateNetwork(network map[string]types.NetworkStats) (float64, float64) { +func calculateNetwork(network map[string]container.NetworkStats) (float64, float64) { var rx, tx float64 for _, v := range network { @@ -1132,11 +1138,11 @@ func pullImages(ctx context.Context, client *client.Client, imageName string) er return nil } -func loadCpuAndMem(client *client.Client, container string) dto.ContainerListStats { +func loadCpuAndMem(client *client.Client, containerItem string) dto.ContainerListStats { data := dto.ContainerListStats{ - ContainerID: container, + ContainerID: containerItem, } - res, err := client.ContainerStats(context.Background(), container, false) + res, err := client.ContainerStats(context.Background(), containerItem, false) if err != nil { return data } @@ -1146,7 +1152,7 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta if err != nil { return data } - var stats *types.StatsJSON + var stats *container.StatsResponse if err := json.Unmarshal(body, &stats); err != nil { return data } @@ -1235,6 +1241,10 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types config.ExposedPorts = exposed config.OpenStdin = req.OpenStdin config.Tty = req.Tty + config.Hostname = req.Hostname + config.Domainname = req.DomainName + config.User = req.User + config.WorkingDir = req.WorkingDir if len(req.Network) != 0 { switch req.Network { @@ -1242,13 +1252,11 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types hostConf.NetworkMode = container.NetworkMode(req.Network) } if req.Ipv4 != "" || req.Ipv6 != "" { - networkConf.EndpointsConfig = map[string]*network.EndpointSettings{ - req.Network: { - IPAMConfig: &network.EndpointIPAMConfig{ - IPv4Address: req.Ipv4, - IPv6Address: req.Ipv6, - }, - }} + networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: { + IPAMConfig: &network.EndpointIPAMConfig{ + IPv4Address: req.Ipv4, + IPv6Address: req.Ipv6, + }, MacAddress: req.MacAddr}} } else { networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}} } @@ -1273,6 +1281,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types hostConf.PortBindings = portMap hostConf.Binds = []string{} hostConf.Mounts = []mount.Mount{} + hostConf.DNS = req.DNS config.Volumes = make(map[string]struct{}) for _, volume := range req.Volumes { if volume.Type == "volume" { diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index cf7de7ffb..de028d779 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -55,12 +55,18 @@ export namespace Container { imageInput: boolean; forcePull: boolean; network: string; + hostname: string; + domainName: string; + macAddr: string; ipv4: string; ipv6: string; + dns: Array; cmdStr: string; entrypointStr: string; memoryItem: number; cmd: Array; + workingDir: string; + user: string; openStdin: boolean; tty: boolean; entrypoint: Array; @@ -73,9 +79,7 @@ export namespace Container { privileged: boolean; autoRemove: boolean; labels: Array; - labelsStr: string; env: Array; - envStr: string; restartPolicy: string; } export interface Port { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 832fda9ed..3d7da1514 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -636,6 +636,7 @@ const message = { cleanLogHelper: 'Clearing logs requires restarting the container, and this operation cannot be rolled back. Do you want to continue?', newName: 'New name', + workingDir: 'Working Dir', source: 'Resource Rate', cpuUsage: 'CPU Usage', cpuTotal: 'CPU Total', @@ -688,6 +689,7 @@ const message = { cpuQuota: 'NacosCPU', memoryLimit: 'Memory', limitHelper: 'If you limit it to 0, then the limitation is turned off, and the maximum available is {0}.', + macAddr: 'MAC Address', mount: 'Mount', volumeOption: 'Volume', hostOption: 'Host', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index dfd53e71e..6dfc781ee 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -615,6 +615,7 @@ const message = { downLogHelper2: '即將下載 {0} 容器最近 {1} 條日誌,是否繼續?', cleanLogHelper: '清空日誌需要重啟容器,該操作無法回滾,是否繼續?', newName: '新名稱', + workingDir: '工作目錄', source: '資源使用率', cpuUsage: 'CPU 使用', cpuTotal: 'CPU 總計', @@ -662,6 +663,7 @@ const message = { cpuQuota: 'CPU 限製', memoryLimit: '內存限製', limitHelper: '限製為 0 則關閉限製,最大可用為 {0}', + macAddr: 'MAC 地址', mount: '掛載', volumeOption: '掛載卷', hostOption: '本機目錄', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index c3ff8488d..3a2dba890 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -616,6 +616,7 @@ const message = { downLogHelper2: '即将下载 {0} 容器最近 {1} 条日志,是否继续?', cleanLogHelper: '清空日志需要重启容器,该操作无法回滚,是否继续?', newName: '新名称', + workingDir: '工作目录', source: '资源使用率', cpuUsage: 'CPU 使用', cpuTotal: 'CPU 总计', @@ -626,8 +627,8 @@ const message = { ip: 'IP 地址', cpuShare: 'CPU 权重', cpuShareHelper: '容器默认份额为 1024 个 CPU,增大可使当前容器获得更多的 CPU 时间', - inputIpv4: '请输入 ipv4 地址', - inputIpv6: '请输入 ipv6 地址', + inputIpv4: '请输入 IPv4 地址', + inputIpv6: '请输入 IPv6 地址', containerFromAppHelper: '检测到该容器来源于应用商店,应用操作可能会导致当前编辑失效', containerFromAppHelper1: '在已安装应用列表点击 `参数` 按钮,进入编辑页面即可修改容器名称。', @@ -663,6 +664,7 @@ const message = { cpuQuota: 'CPU 限制', memoryLimit: '内存限制', limitHelper: '限制为 0 则关闭限制,最大可用为 {0}', + macAddr: 'MAC 地址', mount: '挂载', volumeOption: '挂载卷', hostOption: '本机目录', @@ -2286,7 +2288,7 @@ const message = { targetPort: '目标端口', forwardHelper1: '如果是本机端口转发,目标IP为:127.0.0.1', forwardHelper2: '如果目标IP不填写,则默认为本机端口转发', - forwardHelper3: '当前仅支持 ipv4 的端口转发', + forwardHelper3: '当前仅支持 IPv4 的端口转发', }, runtime: { runtime: '运行环境', diff --git a/frontend/src/views/container/container/operate/index.vue b/frontend/src/views/container/container/operate/index.vue index 17fa9956f..e91b3f2e5 100644 --- a/frontend/src/views/container/container/operate/index.vue +++ b/frontend/src/views/container/container/operate/index.vue @@ -128,30 +128,69 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + {{ $t('commons.button.add') }} + +
+
+
@@ -226,16 +265,40 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ $t('container.tty') }} @@ -285,22 +348,52 @@ - - - - - - + + + +
+ + + +
+ + {{ $t('commons.button.add') }} + +
+
+ + +
+ + + +
+ + {{ $t('commons.button.add') }} + +
+
+
@@ -364,12 +457,18 @@ const form = reactive({ imageInput: false, forcePull: false, network: '', + hostname: '', + domainName: '', + macAddr: '', ipv4: '', ipv6: '', + dns: [], cmdStr: '', entrypointStr: '', memoryItem: 0, cmd: [], + workingDir: '', + user: '', openStdin: false, tty: false, entrypoint: [], @@ -382,58 +481,64 @@ const form = reactive({ privileged: false, autoRemove: false, labels: [], - labelsStr: '', env: [], - envStr: '', restartPolicy: 'no', }); const search = async () => { if (!isCreate.value) { - const res = await loadContainerInfo(form.containerID); - if (res.data) { - form.name = res.data.name; - form.image = res.data.image; - form.network = res.data.network; - form.ipv4 = res.data.ipv4; - form.ipv6 = res.data.ipv6; - form.openStdin = res.data.openStdin; - form.tty = res.data.tty; - form.publishAllPorts = res.data.publishAllPorts; - form.nanoCPUs = res.data.nanoCPUs; - form.cpuShares = res.data.cpuShares; - form.privileged = res.data.privileged; - form.autoRemove = res.data.autoRemove; - form.restartPolicy = res.data.restartPolicy; - form.memory = Number(res.data.memory.toFixed(2)); - form.cmd = res.data.cmd || []; - let itemCmd = ''; - for (const item of form.cmd) { - itemCmd += `'${item}' `; - } - form.cmdStr = itemCmd ? itemCmd.substring(0, itemCmd.length - 1) : ''; - - let itemEntrypoint = ''; - if (res.data.entrypoint) { - for (const item of res.data.entrypoint) { - itemEntrypoint += `'${item}' `; + loading.value = true; + await loadContainerInfo(form.containerID) + .then((res) => { + loading.value = false; + form.name = res.data.name; + form.image = res.data.image; + form.network = res.data.network; + form.hostname = res.data.hostname; + form.domainName = res.data.domainName; + form.dns = res.data.dns; + form.ipv4 = res.data.ipv4; + form.ipv6 = res.data.ipv6; + form.openStdin = res.data.openStdin; + form.tty = res.data.tty; + form.publishAllPorts = res.data.publishAllPorts; + form.nanoCPUs = res.data.nanoCPUs; + form.cpuShares = res.data.cpuShares; + form.privileged = res.data.privileged; + form.autoRemove = res.data.autoRemove; + form.restartPolicy = res.data.restartPolicy; + form.memory = Number(res.data.memory.toFixed(2)); + form.cmd = res.data.cmd || []; + form.user = res.data.user; + form.workingDir = res.data.workingDir; + let itemCmd = ''; + for (const item of form.cmd) { + itemCmd += `'${item}' `; } - } + form.cmdStr = itemCmd ? itemCmd.substring(0, itemCmd.length - 1) : ''; - form.entrypointStr = itemEntrypoint ? itemEntrypoint.substring(0, itemEntrypoint.length - 1) : ''; - form.labels = res.data.labels || []; - form.env = res.data.env || []; - form.labelsStr = res.data.labels.join('\n'); - form.envStr = res.data.env.join('\n'); - form.exposedPorts = res.data.exposedPorts || []; - for (const item of res.data.exposedPorts) { - if (item.hostIP) { - item.host = item.hostIP + ':' + item.hostPort; - } else { - item.host = item.hostPort; + let itemEntrypoint = ''; + if (res.data.entrypoint) { + for (const item of res.data.entrypoint) { + itemEntrypoint += `'${item}' `; + } } - } - form.volumes = res.data.volumes || []; - } + + form.entrypointStr = itemEntrypoint ? itemEntrypoint.substring(0, itemEntrypoint.length - 1) : ''; + form.labels = res.data.labels || []; + form.env = res.data.env || []; + form.exposedPorts = res.data.exposedPorts || []; + for (const item of res.data.exposedPorts) { + if (item.hostIP) { + item.host = item.hostIP + ':' + item.hostPort; + } else { + item.host = item.hostPort; + } + } + form.volumes = res.data.volumes || []; + }) + .catch(() => { + loading.value = false; + }); } loadLimit(); loadImageOptions(); @@ -530,12 +635,6 @@ const onSubmit = async (formEl: FormInstance | undefined) => { if (!formEl) return; formEl.validate(async (valid) => { if (!valid) return; - if (form.envStr) { - form.env = form.envStr.split('\n'); - } - if (form.labelsStr) { - form.labels = form.labelsStr.split('\n'); - } form.cmd = []; if (form.cmdStr) { if (form.cmdStr.indexOf(`'`) !== -1) {