feat: Optimize orchestration creation logs (#11069)

Refs  #11068
This commit is contained in:
ssongliu 2025-11-25 17:21:18 +08:00 committed by GitHub
parent e46d8c5812
commit 2a21f194d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 111 additions and 145 deletions

View file

@ -203,9 +203,8 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
}
go func() {
taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error {
cmd := getComposeCmd(req.Path, "up")
out, err := cmd.CombinedOutput()
taskItem.Log(i18n.GetWithName("ComposeCreateRes", string(out)))
err := compose.UpWithTask(req.Path, t)
t.LogWithStatus(i18n.GetMsgByKey("ComposeCreate"), err)
if err != nil {
_, _ = compose.Down(req.Path)
return err

View file

@ -1,11 +1,21 @@
package compose
import (
"errors"
"fmt"
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"time"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/goccy/go-yaml"
)
func checkCmd() error {
@ -27,6 +37,58 @@ func Up(filePath string) (string, error) {
return stdout, err
}
func UpWithTask(filePath string, task *task.Task) error {
content, err := os.ReadFile(filePath)
if err != nil {
return err
}
env, _ := os.ReadFile(path.Join(path.Dir(filePath), ".env"))
var compose docker.ComposeProject
if err := yaml.Unmarshal(content, &compose); err != nil {
return fmt.Errorf("parse docker-compose file failed: %v", err)
}
images, err := docker.GetImagesFromDockerCompose(env, content)
if err != nil {
return err
}
dockerCLi, err := docker.NewClient()
if err != nil {
return err
}
errMsg := ""
for _, image := range images {
task.Log(i18n.GetWithName("PullImageStart", image))
if err = dockerCLi.PullImageWithProcess(task, image); err != nil {
errOur := err.Error()
if errOur != "" {
if strings.Contains(errOur, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(errOur, "Error response from daemon") {
errMsg = i18n.GetMsgByKey("PullImageTimeout") + ":"
}
}
message := errMsg + errOur
installErr := errors.New(message)
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), installErr)
if exist, _ := dockerCLi.ImageExists(image); !exist {
return installErr
} else {
task.Log(i18n.GetMsgByKey("UseExistImage"))
}
} else {
task.Log(i18n.GetMsgByKey("PullImageSuccess"))
}
}
dockerCommand := global.CONF.DockerConfig.Command
if dockerCommand == "docker-compose" {
return cmd.NewCommandMgr(cmd.WithTask(*task)).Run("docker-compose", "-f", filePath, "up", "-d")
} else {
return cmd.NewCommandMgr(cmd.WithTask(*task)).Run("docker", "compose", "-f", filePath, "up", "-d")
}
}
func Down(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err

View file

@ -69,7 +69,12 @@
</span>
</template>
</DrawerPro>
<TaskLog ref="taskLogRef" width="70%" />
<TaskLog ref="taskLogRef" width="70%">
<template #task-footer>
<el-button @click="handleClose">{{ $t('commons.table.backToList') }}</el-button>
<el-button type="primary" @click="closeTask">{{ $t('commons.table.keepEdit') }}</el-button>
</template>
</TaskLog>
<FileList ref="fileRef" @choose="loadDir" />
</template>
@ -167,8 +172,12 @@ const changeFrom = () => {
const handleClose = () => {
emit('search');
taskLogRef.value?.handleClose();
drawerVisible.value = false;
};
const closeTask = () => {
taskLogRef.value?.handleClose();
};
const loadPath = async () => {
const pathRes = await loadBaseDir();

View file

@ -1,119 +0,0 @@
<template>
<DrawerPro
v-model="composeVisible"
:header="$t('commons.button.edit')"
@close="handleClose"
:resource="name"
size="large"
:autoClose="false"
:fullScreen="true"
>
<div v-loading="loading">
<el-form ref="formRef" @submit.prevent label-position="top">
<el-form-item>
<CodemirrorPro
v-model="content"
mode="yaml"
:heightDiff="175"
placeholder="#Define or paste the content of your docker-compose file here"
></CodemirrorPro>
</el-form-item>
<div v-if="createdBy === '1Panel'">
<el-form-item :label="$t('container.env')" prop="environmentStr">
<el-input
type="textarea"
:placeholder="$t('container.tagHelper')"
:rows="3"
v-model="environmentStr"
/>
</el-form-item>
<span class="input-help whitespace-break-spaces">
{{ $t('container.editComposeHelper') }}
</span>
<CodemirrorPro
v-model="envFileContent"
:height="45"
:minHeight="45"
disabled
mode="yaml"
></CodemirrorPro>
</div>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="composeVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmitEdit()">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { composeUpdate } from '@/api/modules/container';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
const composeVisible = ref(false);
const path = ref();
const content = ref();
const name = ref();
const environmentStr = ref();
const environmentEnv = ref();
const createdBy = ref();
const envFileContent = ref(`env_file:\n - 1panel.env`);
const emit = defineEmits<{ (e: 'search'): void }>();
const onSubmitEdit = async () => {
const param = {
name: name.value,
path: path.value,
content: content.value,
createdBy: createdBy.value,
env: environmentStr.value?.split('\n') || [],
};
loading.value = true;
await composeUpdate(param)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
composeVisible.value = false;
if (environmentStr.value) {
emit('search');
}
})
.finally(() => {
loading.value = false;
});
};
interface DialogProps {
name: string;
path: string;
content: string;
env: Array<string>;
envStr: string;
createdBy: string;
}
const acceptParams = (props: DialogProps): void => {
composeVisible.value = true;
path.value = props.path;
name.value = props.name;
content.value = props.content;
createdBy.value = props.createdBy;
environmentEnv.value = props.env || [];
environmentStr.value = environmentEnv.value.join('\n');
};
const handleClose = () => {
composeVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>

View file

@ -13,14 +13,19 @@
{{ $t('container.createCompose') }}
</el-button>
</template>
<template #rightToolBar>
<TableRefresh @search="search()" />
<TableSetting title="container-refresh" @search="search()" />
</template>
<template #main>
<el-row v-if="data.length > 0" :gutter="20" class="row-box">
<el-col :span="6">
<el-col :span="7">
<el-card>
<el-input
v-model="searchName"
:placeholder="$t('commons.button.search')"
clearable
class="w-4/5"
@clear="search"
@keyup.enter="search"
>
@ -29,17 +34,32 @@
</template>
</el-input>
<ComplexTable :show-header="false" @row-click="loadDetail" :data="data">
<el-table class="mt-2" :show-header="false" @row-click="loadDetail" :data="data">
<el-table-column prop="name">
<template #default="{ row }">
<div class="cursor-pointer">
<div class="font-medium truncate">
<div class="font-medium text-base">
{{ row.name }}
</div>
<div class="mb-1">
<el-text class="w-12" link size="small" type="info">
{{ loadFrom(row) }}
</el-text>
<el-divider direction="vertical" />
<el-text v-if="row.containerCount === 0" type="danger" size="small">
<el-text link size="small" type="info" class="ml-2">
{{ row.createdAt }}
</el-text>
<el-divider direction="vertical" />
<el-text
link
v-if="row.containerCount === 0"
type="danger"
size="small"
>
{{ $t('container.exited') }}
</el-text>
<el-text
link
v-else
:type="
row.containerCount === row.runningCount ? 'success' : 'warning'
@ -50,21 +70,16 @@
$t('container.running', [row.runningCount, row.containerCount])
}}
</el-text>
<el-divider direction="vertical" />
<el-button
link
type="primary"
icon="Folder"
:disabled="!currentCompose?.workdir"
@click="openComposeFolder"
/>
</div>
<div class="mt-1 mb-2">
<el-tag size="small" type="info">{{ loadFrom(row) }}</el-tag>
<el-tag size="small" type="info" class="ml-2">
{{ row.createdAt }}
</el-tag>
</div>
<el-button
plain
round
size="small"
:disabled="!currentCompose?.workdir"
@click="openComposeFolder"
>
{{ $t('home.dir') }}
</el-button>
<el-button
plain
round
@ -101,10 +116,10 @@
</div>
</template>
</el-table-column>
</ComplexTable>
</el-table>
</el-card>
</el-col>
<el-col :span="18">
<el-col :span="17">
<el-card v-if="currentCompose" v-loading="detailLoading">
<el-table
v-if="composeContainers.length > 0"

View file

@ -365,7 +365,7 @@
<TerminalDialog ref="dialogTerminalRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<TaskLog ref="taskLogRef" width="70%" />
<TaskLog ref="taskLogRef" width="70%" @close="search" />
</div>
</template>