fix: 创建编排改为异步操作 (#318)

fix:  创建编排改为异步操作
This commit is contained in:
ssongliu 2023-03-20 18:16:26 +08:00 committed by GitHub
parent 2096049708
commit 0c5a5a6454
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 116 additions and 45 deletions

View File

@ -90,11 +90,12 @@ func (b *BaseApi) CreateCompose(c *gin.Context) {
return
}
if err := containerService.CreateCompose(req); err != nil {
log, err := containerService.CreateCompose(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
helper.SuccessWithData(c, log)
}
// @Tags Container Compose

View File

@ -33,7 +33,7 @@ type IContainerService interface {
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
ListVolume() ([]dto.Options, error)
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
CreateCompose(req dto.ComposeCreate) error
CreateCompose(req dto.ComposeCreate) (string, error)
ComposeOperation(req dto.ComposeOperation) error
ContainerCreate(req dto.ContainerCreate) error
ContainerOperation(req dto.ContainerOperation) error

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path"
"sort"
"strings"
@ -124,11 +125,11 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
return int64(total), BackDatas, nil
}
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
if req.From == "template" {
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
if err != nil {
return err
return "", err
}
req.From = "edit"
req.File = template.Content
@ -137,14 +138,14 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return err
return "", err
}
}
path := fmt.Sprintf("%s/docker-compose.yml", dir)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
return "", err
}
defer file.Close()
write := bufio.NewWriter(file)
@ -157,13 +158,28 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
if req.From == "path" {
req.Name = path.Base(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
}
if stdout, err := compose.Up(req.Path); err != nil {
_, _ = compose.Down(req.Path)
return errors.New(stdout)
logName := strings.ReplaceAll(req.Path, "docker-compose.yml", "compose.log")
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return "", err
}
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
go func() {
defer file.Close()
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
stdout, err := cmd.CombinedOutput()
_, _ = file.Write(stdout)
if err != nil {
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
_, _ = compose.Down(req.Path)
_, _ = file.WriteString("docker-compose up failed!")
return
}
global.LOG.Infof("docker-compose up %s successful!", req.Name)
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
_, _ = file.WriteString("docker-compose up successful!")
}()
return nil
return logName, nil
}
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {

View File

@ -7,7 +7,7 @@ import (
)
func Up(filePath string) (string, error) {
stdout, err := cmd.Execf("docker-compose -f %s up -d --quiet-pull", filePath)
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
return stdout, err
}

View File

@ -117,7 +117,7 @@ export const searchCompose = (params: SearchWithPage) => {
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
};
export const upCompose = (params: Container.ComposeCreate) => {
return http.post(`/containers/compose`, params, 600000);
return http.post<string>(`/containers/compose`, params, 600000);
};
export const composeOperator = (params: Container.ComposeOpration) => {
return http.post(`/containers/compose/operate`, params);

View File

@ -1,12 +1,18 @@
<template>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<el-drawer
v-model="drawerVisiable"
@close="handleClose"
:destroy-on-close="true"
:close-on-click-modal="false"
size="50%"
>
<template #header>
<DrawerHeader :header="$t('container.compose')" :back="handleClose" />
</template>
<div v-loading="loading">
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
<el-row type="flex" justify="center">
<el-col :span="22">
<div>
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
<el-form-item :label="$t('container.from')">
<el-radio-group v-model="form.from">
<el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio>
@ -46,7 +52,7 @@
placeholder="#Define or paste the content of your docker-compose file here"
:indent-with-tab="true"
:tabSize="4"
style="width: 100%; height: calc(100vh - 340px)"
style="width: 100%; height: 200px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@ -55,16 +61,32 @@
v-model="form.file"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-form>
<codemirror
v-if="logVisiable"
:autofocus="true"
placeholder="Waiting for build output..."
:indent-with-tab="true"
:tabSize="4"
style="max-height: calc(100vh - 537px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
@ready="handleReady"
v-model="logInfo"
:readOnly="true"
/>
</el-col>
</el-row>
</div>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="drawerVisiable = false">
<el-button @click="drawerVisiable = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" :disabled="loading" @click="onSubmit(formRef)">
<el-button type="primary" :disabled="buttonDisabled" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
@ -73,7 +95,7 @@
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { nextTick, reactive, ref, shallowRef } from 'vue';
import FileList from '@/components/file-list/index.vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
@ -83,23 +105,34 @@ import i18n from '@/lang';
import { ElForm } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { listComposeTemplate, upCompose } from '@/api/modules/container';
import { MsgSuccess } from '@/utils/message';
import { loadBaseDir } from '@/api/modules/setting';
import { LoadFile } from '@/api/modules/files';
import { formatImageStdout } from '@/utils/docker';
const extensions = [javascript(), oneDark];
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const logVisiable = ref();
const logInfo = ref();
const drawerVisiable = ref(false);
const templateOptions = ref();
const buttonDisabled = ref(false);
const loading = ref(false);
const baseDir = ref();
const composeFile = ref();
let timer: NodeJS.Timer | null = null;
const varifyPath = (rule: any, value: any, callback: any) => {
if (value.indexOf('docker-compose.yml') === -1) {
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['docker-compose.yml'])));
}
callback();
};
const form = reactive({
name: '',
from: 'edit',
@ -126,12 +159,17 @@ const acceptParams = (): void => {
form.from = 'edit';
form.path = '';
form.file = '';
logVisiable.value = false;
logInfo.value = '';
loadTemplates();
loadPath();
};
const emit = defineEmits<{ (e: 'search'): void }>();
const handleClose = () => {
emit('search');
clearInterval(Number(timer));
timer = null;
drawerVisiable.value = false;
};
@ -152,20 +190,38 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
upCompose(form)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loading.value = false;
emit('search');
drawerVisiable.value = false;
})
.finally(() => {
loading.value = false;
});
const res = await upCompose(form);
logInfo.value = '';
buttonDisabled.value = true;
logVisiable.value = true;
loadLogs(res.data);
});
};
const loadLogs = async (path: string) => {
timer = setInterval(async () => {
if (logVisiable.value) {
const res = await LoadFile({ path: path });
logInfo.value = formatImageStdout(res.data);
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
if (
logInfo.value.endsWith('docker-compose up failed!') ||
logInfo.value.endsWith('docker-compose up successful!')
) {
clearInterval(Number(timer));
timer = null;
buttonDisabled.value = false;
}
}
}, 1000 * 3);
};
const loadDir = async (path: string) => {
form.path = path;
};

View File

@ -2,7 +2,7 @@
<el-drawer
v-model="drawerVisiable"
:destroy-on-close="true"
@close="onCloseLog"
@close="handleClose"
:close-on-click-modal="false"
size="50%"
>
@ -141,6 +141,9 @@ const emit = defineEmits<{ (e: 'search'): void }>();
const handleClose = () => {
drawerVisiable.value = false;
emit('search');
clearInterval(Number(timer));
timer = null;
};
type FormInstance = InstanceType<typeof ElForm>;
@ -181,11 +184,6 @@ const loadLogs = async (path: string) => {
}
}, 1000 * 3);
};
const onCloseLog = async () => {
emit('search');
clearInterval(Number(timer));
timer = null;
};
const loadBuildDir = async (path: string) => {
form.dockerfile = path;