feat: 创建容器时,端口支持多种形式 (#790)

This commit is contained in:
ssongliu 2023-04-26 15:18:13 +08:00 committed by GitHub
parent 14cc97eb44
commit dd0ca4bcaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 30 deletions

View File

@ -63,8 +63,10 @@ type VolumeHelper struct {
Mode string `json:"mode"`
}
type PortHelper struct {
ContainerPort int `json:"containerPort"`
HostPort int `json:"hostPort"`
HostIP string `json:"hostIP"`
HostPort string `json:"hostPort"`
ContainerPort string `json:"containerPort"`
Protocol string `json:"protocol"`
}
type ContainerLog struct {

View File

@ -168,8 +168,19 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
if len(req.ExposedPorts) != 0 {
for _, port := range req.ExposedPorts {
if common.ScanPort(port.HostPort) {
return buserr.WithDetail(constant.ErrPortInUsed, port.HostPort, nil)
if strings.Contains(port.HostPort, "-") {
portStart, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[0])
portEnd, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[1])
for i := portStart; i <= portEnd; i++ {
if common.ScanPort(i) {
return buserr.WithDetail(constant.ErrPortInUsed, i, nil)
}
}
} else {
portItem, _ := strconv.Atoi(port.HostPort)
if common.ScanPort(portItem) {
return buserr.WithDetail(constant.ErrPortInUsed, portItem, nil)
}
}
}
}
@ -202,8 +213,8 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
if len(req.ExposedPorts) != 0 {
hostConf.PortBindings = make(nat.PortMap)
for _, port := range req.ExposedPorts {
bindItem := nat.PortBinding{HostPort: strconv.Itoa(port.HostPort)}
hostConf.PortBindings[nat.Port(fmt.Sprintf("%d/tcp", port.ContainerPort))] = []nat.PortBinding{bindItem}
bindItem := nat.PortBinding{HostPort: port.HostPort, HostIP: port.HostIP}
hostConf.PortBindings[nat.Port(fmt.Sprintf("%s/%s", port.ContainerPort, port.Protocol))] = []nat.PortBinding{bindItem}
}
}
if len(req.Volumes) != 0 {

View File

@ -27,8 +27,11 @@ export namespace Container {
restartPolicy: string;
}
export interface Port {
containerPort: number;
hostPort: number;
host: string;
hostIP: string;
containerPort: string;
hostPort: string;
protocol: string;
}
export interface Volume {
sourceDir: string;

View File

@ -455,10 +455,11 @@ const message = {
containerTerminal: 'Terminal',
port: 'Port',
server: 'Host',
serverExample: 'e.g. 80, 80-88, ip:80 or ip:80-88',
contianerExample: 'e.g. 80 or 80-88',
exposePort: 'Expose port',
exposeAll: 'Expose all',
containerPort: 'Container port',
serverPort: 'Host port',
cmd: 'Command',
cmdHelper: 'Example: echo "hello"',
autoRemove: 'Auto remove',

View File

@ -472,10 +472,11 @@ const message = {
emptyUser: '为空时将使用容器默认的用户登录',
port: '端口',
server: '服务器',
serverExample: '例如 80, 80-88, ip:80 或者 ip:80-88',
contianerExample: '例如 80 或者 80-88',
exposePort: '暴露端口',
exposeAll: '暴露所有',
containerPort: '容器端口',
serverPort: '服务器端口',
cmd: '启动命令',
cmdHelper: 'echo "hello"',
autoRemove: '容器退出后自动删除容器',

View File

@ -29,33 +29,38 @@
<el-card style="width: 100%">
<table style="width: 100%" class="tab-table">
<tr v-if="form.exposedPorts.length !== 0">
<th scope="col" width="48%" align="left">
<label>{{ $t('container.serverPort') }}</label>
<th scope="col" width="45%" align="left">
<label>{{ $t('container.server') }}</label>
</th>
<th scope="col" width="48%" align="left">
<label>{{ $t('container.containerPort') }}</label>
<th scope="col" width="35%" align="left">
<label>{{ $t('container.container') }}</label>
</th>
<th scope="col" width="20%" align="left">
<label>{{ $t('container.protocol') }}</label>
</th>
<th align="left"></th>
</tr>
<tr v-for="(row, index) in form.exposedPorts" :key="index">
<td width="48%">
<el-input-number
:min="0"
:max="65535"
<td width="45%">
<el-input
:placeholder="$t('container.serverExample')"
style="width: 100%"
controls-position="right"
v-model.number="row.hostPort"
v-model="row.host"
/>
</td>
<td width="48%">
<el-input-number
:min="0"
:max="65535"
<td width="35%">
<el-input
:placeholder="$t('container.contianerExample')"
style="width: 100%"
controls-position="right"
v-model.number="row.containerPort"
v-model="row.containerPort"
/>
</td>
<td width="20%">
<el-select v-model="row.protocol" style="width: 100%">
<el-option label="tcp" value="tcp" />
<el-option label="upd" value="upd" />
</el-select>
</td>
<td>
<el-button link style="font-size: 10px" @click="handlePortsDelete(index)">
{{ $t('commons.button.delete') }}
@ -204,6 +209,7 @@ import DrawerHeader from '@/components/drawer-header/index.vue';
import { listImage, listVolume, createContainer } from '@/api/modules/container';
import { Container } from '@/api/interface/container';
import { MsgError, MsgSuccess } from '@/utils/message';
import { checkIp, checkPort } from '@/utils/util';
const loading = ref(false);
@ -277,8 +283,11 @@ const formRef = ref<FormInstance>();
const handlePortsAdd = () => {
let item = {
containerPort: null,
hostPort: null,
host: '',
hostIP: '',
containerPort: '',
hostPort: '',
protocol: 'tcp',
};
form.exposedPorts.push(item);
};
@ -327,6 +336,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (form.cmdStr.length !== 0) {
form.cmd = form.cmdStr.split(' ');
}
if (!checkPortValid()) {
return;
}
switch (form.memoryUnit) {
case 'KB':
form.memory = form.memoryItem * 1024;
@ -352,6 +364,54 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
});
};
const checkPortValid = async () => {
if (form.exposedPorts.length === 0) {
return true;
}
for (const port of form.exposedPorts) {
if (port.host.indexOf(':') !== -1) {
port.hostIP = port.host.split(':')[0];
if (checkIp(port.hostIP)) {
MsgError(i18n.global.t('firewall.addressFormatError'));
return false;
}
port.hostPort = port.host.split(':')[1];
} else {
port.hostPort = port.host;
}
if (port.hostPort.indexOf('-') !== -1) {
if (checkPort(port.hostPort.split('-')[0])) {
MsgError(i18n.global.t('firewall.portFormatError'));
return false;
}
if (checkPort(port.hostPort.split('-')[1])) {
MsgError(i18n.global.t('firewall.portFormatError'));
return false;
}
} else {
if (checkPort(port.hostPort)) {
MsgError(i18n.global.t('firewall.portFormatError'));
return false;
}
}
if (port.containerPort.indexOf('-') !== -1) {
if (checkPort(port.containerPort.split('-')[0])) {
MsgError(i18n.global.t('firewall.portFormatError'));
return false;
}
if (checkPort(port.containerPort.split('-')[1])) {
MsgError(i18n.global.t('firewall.portFormatError'));
return false;
}
} else {
if (checkPort(port.containerPort)) {
MsgError(i18n.global.t('firewall.portFormatError'));
return false;
}
}
}
return true;
};
defineExpose({
acceptParams,
});