fix: 解决容器挂载卷回显失败的问题 (#4389)

Refs #4387
This commit is contained in:
ssongliu 2024-04-02 17:14:45 +08:00 committed by GitHub
parent 13bb5e0d30
commit 47119e508b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 59 additions and 82 deletions

View file

@ -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"`

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;
}); });
} }
}) })

View file

@ -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();