mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-11 16:06:02 +08:00
parent
13bb5e0d30
commit
47119e508b
6 changed files with 59 additions and 82 deletions
|
@ -98,6 +98,7 @@ type ContainerStats struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type VolumeHelper struct {
|
type VolumeHelper struct {
|
||||||
|
Type string `json:"type"`
|
||||||
SourceDir string `json:"sourceDir"`
|
SourceDir string `json:"sourceDir"`
|
||||||
ContainerDir string `json:"containerDir"`
|
ContainerDir string `json:"containerDir"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/registry"
|
"github.com/docker/docker/api/types/registry"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
@ -442,7 +443,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||||
if oldContainer.HostConfig.Memory != 0 {
|
if oldContainer.HostConfig.Memory != 0 {
|
||||||
data.Memory = float64(oldContainer.HostConfig.Memory) / 1024 / 1024
|
data.Memory = float64(oldContainer.HostConfig.Memory) / 1024 / 1024
|
||||||
}
|
}
|
||||||
data.Volumes = loadVolumeBinds(oldContainer.HostConfig.Binds)
|
data.Volumes = loadVolumeBinds(oldContainer.Mounts)
|
||||||
|
|
||||||
return &data, nil
|
return &data, nil
|
||||||
}
|
}
|
||||||
|
@ -994,10 +995,19 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||||
hostConf.MemorySwap = 0
|
hostConf.MemorySwap = 0
|
||||||
hostConf.PortBindings = portMap
|
hostConf.PortBindings = portMap
|
||||||
hostConf.Binds = []string{}
|
hostConf.Binds = []string{}
|
||||||
|
hostConf.Mounts = []mount.Mount{}
|
||||||
config.Volumes = make(map[string]struct{})
|
config.Volumes = make(map[string]struct{})
|
||||||
for _, volume := range req.Volumes {
|
for _, volume := range req.Volumes {
|
||||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
if volume.Type == "volume" {
|
||||||
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
|
||||||
|
Type: mount.Type(volume.Type),
|
||||||
|
Source: volume.SourceDir,
|
||||||
|
Target: volume.ContainerDir,
|
||||||
|
})
|
||||||
|
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||||
|
} else {
|
||||||
|
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &config, &hostConf, &networkConf, nil
|
return &config, &hostConf, &networkConf, nil
|
||||||
}
|
}
|
||||||
|
@ -1024,40 +1034,21 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
|
||||||
global.LOG.Errorf("recreate after container update successful")
|
global.LOG.Errorf("recreate after container update successful")
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadVolumeBinds(binds []string) []dto.VolumeHelper {
|
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
|
||||||
var datas []dto.VolumeHelper
|
var datas []dto.VolumeHelper
|
||||||
for _, bind := range binds {
|
for _, bind := range binds {
|
||||||
parts := strings.Split(bind, ":")
|
|
||||||
var volumeItem dto.VolumeHelper
|
var volumeItem dto.VolumeHelper
|
||||||
if len(parts) > 3 {
|
volumeItem.Type = string(bind.Type)
|
||||||
continue
|
if bind.Type == "volume" {
|
||||||
|
volumeItem.SourceDir = bind.Name
|
||||||
|
} else {
|
||||||
|
volumeItem.SourceDir = bind.Source
|
||||||
}
|
}
|
||||||
volumeItem.SourceDir = parts[0]
|
volumeItem.ContainerDir = bind.Destination
|
||||||
if len(parts) == 1 {
|
volumeItem.Mode = "ro"
|
||||||
volumeItem.ContainerDir = parts[0]
|
if bind.RW {
|
||||||
volumeItem.Mode = "rw"
|
volumeItem.Mode = "rw"
|
||||||
}
|
}
|
||||||
if len(parts) == 2 {
|
|
||||||
switch parts[1] {
|
|
||||||
case "r", "ro":
|
|
||||||
volumeItem.ContainerDir = parts[0]
|
|
||||||
volumeItem.Mode = "ro"
|
|
||||||
case "rw":
|
|
||||||
volumeItem.ContainerDir = parts[0]
|
|
||||||
volumeItem.Mode = "rw"
|
|
||||||
default:
|
|
||||||
volumeItem.ContainerDir = parts[1]
|
|
||||||
volumeItem.Mode = "rw"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(parts) == 3 {
|
|
||||||
volumeItem.ContainerDir = parts[1]
|
|
||||||
if parts[2] == "r" {
|
|
||||||
volumeItem.Mode = "ro"
|
|
||||||
} else {
|
|
||||||
volumeItem.Mode = parts[2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
datas = append(datas, volumeItem)
|
datas = append(datas, volumeItem)
|
||||||
}
|
}
|
||||||
return datas
|
return datas
|
||||||
|
|
|
@ -56,10 +56,10 @@ export namespace Container {
|
||||||
protocol: string;
|
protocol: string;
|
||||||
}
|
}
|
||||||
export interface Volume {
|
export interface Volume {
|
||||||
|
type: string;
|
||||||
sourceDir: string;
|
sourceDir: string;
|
||||||
containerDir: string;
|
containerDir: string;
|
||||||
mode: string;
|
mode: string;
|
||||||
isVolume: boolean;
|
|
||||||
}
|
}
|
||||||
export interface ContainerInfo {
|
export interface ContainerInfo {
|
||||||
containerID: string;
|
containerID: string;
|
||||||
|
|
|
@ -98,7 +98,7 @@ const readReq = reactive({
|
||||||
page: 0,
|
page: 0,
|
||||||
pageSize: 2000,
|
pageSize: 2000,
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['update:loading', 'update:hasContent']);
|
const emit = defineEmits(['update:loading', 'update:hasContent', 'update:isReading']);
|
||||||
|
|
||||||
const handleReady = (payload) => {
|
const handleReady = (payload) => {
|
||||||
view.value = payload.view;
|
view.value = payload.view;
|
||||||
|
@ -152,6 +152,7 @@ const stopSignals = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const getContent = () => {
|
const getContent = () => {
|
||||||
|
emit('update:isReading', true);
|
||||||
if (!end.value) {
|
if (!end.value) {
|
||||||
readReq.page += 1;
|
readReq.page += 1;
|
||||||
}
|
}
|
||||||
|
@ -219,6 +220,7 @@ const onDownload = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCloseLog = async () => {
|
const onCloseLog = async () => {
|
||||||
|
emit('update:isReading', false);
|
||||||
tailLog.value = false;
|
tailLog.value = false;
|
||||||
clearInterval(Number(timer));
|
clearInterval(Number(timer));
|
||||||
timer = null;
|
timer = null;
|
||||||
|
|
|
@ -30,33 +30,27 @@
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-row :gutter="20">
|
<el-form-item v-if="form.from === 'template'" prop="template">
|
||||||
<el-col :span="12">
|
<el-select v-model="form.template" @change="changeTemplate">
|
||||||
<el-form-item v-if="form.from === 'edit' || form.from === 'template'" prop="name">
|
<template #prefix>{{ $t('container.template') }}</template>
|
||||||
<el-input @input="changePath" v-model.trim="form.name">
|
<el-option
|
||||||
<template #prefix>
|
v-for="item in templateOptions"
|
||||||
<span style="margin-right: 8px">{{ $t('file.dir') }}</span>
|
:key="item.id"
|
||||||
</template>
|
:value="item.id"
|
||||||
</el-input>
|
:label="item.name"
|
||||||
<span class="input-help">
|
/>
|
||||||
{{ $t('container.composePathHelper', [composeFile]) }}
|
</el-select>
|
||||||
</span>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item v-if="form.from === 'edit' || form.from === 'template'" prop="name">
|
||||||
</el-col>
|
<el-input @input="changePath" v-model.trim="form.name">
|
||||||
<el-col :span="12">
|
<template #prefix>
|
||||||
<el-form-item v-if="form.from === 'template'" prop="template">
|
<span style="margin-right: 8px">{{ $t('file.dir') }}</span>
|
||||||
<el-select v-model="form.template" @change="changeTemplate">
|
</template>
|
||||||
<template #prefix>{{ $t('container.template') }}</template>
|
</el-input>
|
||||||
<el-option
|
<span class="input-help">
|
||||||
v-for="item in templateOptions"
|
{{ $t('container.composePathHelper', [composeFile]) }}
|
||||||
:key="item.id"
|
</span>
|
||||||
:value="item.id"
|
</el-form-item>
|
||||||
:label="item.name"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<div v-if="form.from === 'edit' || form.from === 'template'" style="width: 100%">
|
<div v-if="form.from === 'edit' || form.from === 'template'" style="width: 100%">
|
||||||
<el-radio-group v-model="mode" size="small">
|
<el-radio-group v-model="mode" size="small">
|
||||||
|
@ -81,6 +75,7 @@
|
||||||
<div style="width: 100%">
|
<div style="width: 100%">
|
||||||
<LogFile
|
<LogFile
|
||||||
ref="logRef"
|
ref="logRef"
|
||||||
|
v-model:is-reading="isReading"
|
||||||
:config="logConfig"
|
:config="logConfig"
|
||||||
:default-button="false"
|
:default-button="false"
|
||||||
v-if="mode === 'log' && showLog"
|
v-if="mode === 'log' && showLog"
|
||||||
|
@ -97,7 +92,7 @@
|
||||||
<el-button @click="drawerVisible = false">
|
<el-button @click="drawerVisible = false">
|
||||||
{{ $t('commons.button.cancel') }}
|
{{ $t('commons.button.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" :disabled="onCreating" @click="onSubmit(formRef)">
|
<el-button type="primary" :disabled="isReading" @click="onSubmit(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
@ -124,7 +119,6 @@ const extensions = [javascript(), oneDark];
|
||||||
const showLog = ref(false);
|
const showLog = ref(false);
|
||||||
const loading = ref();
|
const loading = ref();
|
||||||
const mode = ref('edit');
|
const mode = ref('edit');
|
||||||
const onCreating = ref();
|
|
||||||
const oldFrom = ref('edit');
|
const oldFrom = ref('edit');
|
||||||
const drawerVisible = ref(false);
|
const drawerVisible = ref(false);
|
||||||
const templateOptions = ref();
|
const templateOptions = ref();
|
||||||
|
@ -132,6 +126,7 @@ const baseDir = ref();
|
||||||
const composeFile = ref();
|
const composeFile = ref();
|
||||||
let timer: NodeJS.Timer | null = null;
|
let timer: NodeJS.Timer | null = null;
|
||||||
const logRef = ref();
|
const logRef = ref();
|
||||||
|
const isReading = ref();
|
||||||
|
|
||||||
const logConfig = reactive({
|
const logConfig = reactive({
|
||||||
type: 'compose-create',
|
type: 'compose-create',
|
||||||
|
@ -164,7 +159,6 @@ const acceptParams = (): void => {
|
||||||
form.path = '';
|
form.path = '';
|
||||||
form.file = '';
|
form.file = '';
|
||||||
form.template = null;
|
form.template = null;
|
||||||
onCreating.value = false;
|
|
||||||
loadTemplates();
|
loadTemplates();
|
||||||
loadPath();
|
loadPath();
|
||||||
};
|
};
|
||||||
|
@ -237,7 +231,6 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
onCreating.value = true;
|
|
||||||
mode.value = 'log';
|
mode.value = 'log';
|
||||||
await upCompose(form)
|
await upCompose(form)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -246,7 +239,6 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
onCreating.value = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -138,9 +138,9 @@
|
||||||
<el-form-item :label="$t('container.mount')">
|
<el-form-item :label="$t('container.mount')">
|
||||||
<div v-for="(row, index) in dialogData.rowData!.volumes" :key="index" style="width: 100%">
|
<div v-for="(row, index) in dialogData.rowData!.volumes" :key="index" style="width: 100%">
|
||||||
<el-card class="mt-1">
|
<el-card class="mt-1">
|
||||||
<el-radio-group v-model="row.isVolume">
|
<el-radio-group v-model="row.type">
|
||||||
<el-radio-button :value="true">{{ $t('container.volumeOption') }}</el-radio-button>
|
<el-radio-button value="volume">{{ $t('container.volumeOption') }}</el-radio-button>
|
||||||
<el-radio-button :value="false">{{ $t('container.hostOption') }}</el-radio-button>
|
<el-radio-button value="bind">{{ $t('container.hostOption') }}</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<el-button
|
<el-button
|
||||||
class="float-right mt-3"
|
class="float-right mt-3"
|
||||||
|
@ -152,7 +152,10 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-row class="mt-4" :gutter="5">
|
<el-row class="mt-4" :gutter="5">
|
||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
<el-form-item v-if="row.isVolume" :label="$t('container.volumeOption')">
|
<el-form-item
|
||||||
|
v-if="row.type === 'volume'"
|
||||||
|
:label="$t('container.volumeOption')"
|
||||||
|
>
|
||||||
<el-select filterable v-model="row.sourceDir">
|
<el-select filterable v-model="row.sourceDir">
|
||||||
<div v-for="(item, indexV) of volumes" :key="indexV">
|
<div v-for="(item, indexV) of volumes" :key="indexV">
|
||||||
<el-tooltip :hide-after="20" :content="item.option" placement="top">
|
<el-tooltip :hide-after="20" :content="item.option" placement="top">
|
||||||
|
@ -403,10 +406,10 @@ const goRouter = async () => {
|
||||||
|
|
||||||
const handleVolumesAdd = () => {
|
const handleVolumesAdd = () => {
|
||||||
let item = {
|
let item = {
|
||||||
|
type: 'bind',
|
||||||
sourceDir: '',
|
sourceDir: '',
|
||||||
containerDir: '',
|
containerDir: '',
|
||||||
mode: 'rw',
|
mode: 'rw',
|
||||||
isVolume: true,
|
|
||||||
};
|
};
|
||||||
dialogData.value.rowData!.volumes.push(item);
|
dialogData.value.rowData!.volumes.push(item);
|
||||||
};
|
};
|
||||||
|
@ -427,18 +430,6 @@ const loadImageOptions = async () => {
|
||||||
const loadVolumeOptions = async () => {
|
const loadVolumeOptions = async () => {
|
||||||
const res = await listVolume();
|
const res = await listVolume();
|
||||||
volumes.value = res.data;
|
volumes.value = res.data;
|
||||||
for (const item of dialogData.value.rowData.volumes) {
|
|
||||||
let isVolume = false;
|
|
||||||
for (const v of volumes.value) {
|
|
||||||
if (item.sourceDir == v.option) {
|
|
||||||
item.isVolume = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!isVolume) {
|
|
||||||
item.isVolume = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const loadNetworkOptions = async () => {
|
const loadNetworkOptions = async () => {
|
||||||
const res = await listNetwork();
|
const res = await listNetwork();
|
||||||
|
|
Loading…
Add table
Reference in a new issue