Feat: docker env & docker workspace config

This commit is contained in:
unitwk 2024-02-18 15:35:44 +08:00
parent 6b0f0ea57e
commit 4aa94256d5
7 changed files with 84 additions and 23 deletions

1
common/global.d.ts vendored
View File

@ -72,6 +72,7 @@ export interface IGlobalInstanceDockerConfig {
cpusetCpus?: string;
cpuUsage?: number;
workingDir?: string;
env?: string[];
}
export interface IPanelResponseProtocol {

View File

@ -33,15 +33,6 @@ export function configureEntityParams(self: any, args: any, key: string, typeFn?
return;
}
if (typeFn === Array) {
if (v == null) return (self[key] = null);
if (!(v instanceof Array))
throw new Error(
`ConfigureEntityParams Error: Expected type to be Array, but got ${typeof v}`
);
return;
}
if (typeFn) {
self[key] = typeFn(v);
} else {

View File

@ -8,6 +8,7 @@ import { IInstanceProcess } from "../../instance/interface";
import fs from "fs-extra";
import { commandStringToArray } from "../base/command_parser";
import path from "path";
import { t } from "i18next";
// user identity function
const processUserUid = process.getuid ? process.getuid : () => 0;
@ -121,13 +122,14 @@ export default class DockerStartCommand extends InstanceCommand {
const publicPortArray: any = {};
const exposedPorts: any = {};
for (const iterator of portMap) {
const elemt = iterator.split("/");
if (elemt.length != 2) continue;
const ports = elemt[0];
const protocol = elemt[1];
const elem = iterator.split("/");
if (elem.length != 2) throw new Error(t("此容器的开放端口配置有误!"));
const ports = elem[0];
const protocol = elem[1];
//Host (host) port: container port
const publicAndPrivatePort = ports.split(":");
if (publicAndPrivatePort.length != 2) continue;
if (publicAndPrivatePort.length != 2)
throw new Error(t("此容器的开放端口配置有误,分隔符号左右两边不存在值!"));
publicPortArray[`${publicAndPrivatePort[1]}/${protocol}`] = [
{ HostPort: publicAndPrivatePort[0] }
];
@ -138,9 +140,9 @@ export default class DockerStartCommand extends InstanceCommand {
const extraVolumes = instance.config.docker.extraVolumes;
const extraBinds = [];
for (const item of extraVolumes) {
if (!item) continue;
if (!item) throw new Error("实例容器额外挂载路径配置错误!请检查!");
const paths = item.split(":");
if (paths.length < 2) continue;
if (paths.length < 2) throw new Error("实例容器额外挂载路径配置错误!请检查!");
const hostPath = path.normalize(paths[0]);
const containerPath = path.normalize(paths.slice(1).join(":"));
extraBinds.push(`${hostPath}:${containerPath}`);
@ -170,7 +172,8 @@ export default class DockerStartCommand extends InstanceCommand {
}
// container name check
let containerName = instance.config.docker.containerName;
let containerName =
instance.config.docker.containerName || `MCSM-${instance.instanceUuid.slice(0, 6)}`;
if (containerName && (containerName.length > 64 || containerName.length < 2)) {
throw new Error($t("TXT_CODE_instance.invalidContainerName", { v: containerName }));
}
@ -185,7 +188,7 @@ export default class DockerStartCommand extends InstanceCommand {
logger.info(`UUID: [${instance.instanceUuid}] [${instance.config.nickname}]`);
logger.info(`NAME: [${containerName}]`);
logger.info(`COMMAND: ${commandList.join(" ")}`);
logger.info(`CWD: ${cwd}`);
logger.info(`CWD: ${cwd}, WORKING_DIR: ${workingDir}`);
logger.info(`NET_MODE: ${instance.config.docker.networkMode}`);
logger.info(`OPEN_PORT: ${JSON.stringify(publicPortArray)}`);
logger.info(`BINDS: ${JSON.stringify([`${cwd}:${workingDir}`, ...extraBinds])}`);
@ -210,6 +213,7 @@ export default class DockerStartCommand extends InstanceCommand {
OpenStdin: true,
StdinOnce: false,
ExposedPorts: exposedPorts,
Env: instance.config.docker?.env || [],
HostConfig: {
Memory: maxMemory,
Binds: [`${cwd}:${workingDir}`, ...extraBinds],
@ -237,6 +241,7 @@ export default class DockerStartCommand extends InstanceCommand {
h: instance.config.terminalOption.ptyWindowCol
});
instance.println("Container", t("已挂载工作目录:") + workingDir);
instance.started(processAdapter);
logger.info(
$t("TXT_CODE_instance.successful", {

View File

@ -64,7 +64,8 @@ export default class InstanceConfig implements IGlobalInstanceConfig {
maxSpace: null,
io: null,
network: null,
workingDir: "/workspace/"
workingDir: "/workspace/",
env: []
};
public pingConfig = {

View File

@ -92,8 +92,7 @@ export default class Instance extends EventEmitter {
}
if (cfg?.enableRcon != null && cfg?.enableRcon !== this.config.enableRcon) {
if (this.status() != Instance.STATUS_STOP)
throw new Error($t("TXT_CODE_bdfa3457"));
if (this.status() != Instance.STATUS_STOP) throw new Error($t("TXT_CODE_bdfa3457"));
configureEntityParams(this.config, cfg, "enableRcon", Boolean);
this.forceExec(new FunctionDispatcher());
}
@ -170,6 +169,8 @@ export default class Instance extends EventEmitter {
configureEntityParams(this.config.docker, cfg.docker, "networkAliases");
configureEntityParams(this.config.docker, cfg.docker, "cpusetCpus", String);
configureEntityParams(this.config.docker, cfg.docker, "cpuUsage", Number);
configureEntityParams(this.config.docker, cfg.docker, "env");
configureEntityParams(this.config.docker, cfg.docker, "workingDir", String);
}
if (cfg.pingConfig) {
configureEntityParams(this.config.pingConfig, cfg.pingConfig, "ip", String);

View File

@ -17,6 +17,11 @@ interface PortConfigItem extends DockerConfigItem {
protocol: string;
}
interface DockerEnvItem {
label: string;
value: string;
}
export async function useUploadFileDialog() {
return (await useMountComponent().mount<string>(UploadFileDialogVue)) || "";
}
@ -92,6 +97,27 @@ export async function useVolumeEditDialog(data: DockerConfigItem[] = []) {
);
}
export async function useDockerEnvEditDialog(data: DockerEnvItem[] = []) {
return (
(await useMountComponent({
data,
title: t("容器环境变量"),
columns: [
{
align: "center",
dataIndex: "label",
title: t("变量名")
},
{
align: "center",
dataIndex: "value",
title: t("变量值")
}
] as AntColumnsType[]
}).mount<DockerEnvItem[]>(KvOptionsDialogVue)) || []
);
}
export async function openLoadingDialog(title: string, text: string, subTitle?: string) {
const component = (
await useMountComponent({

View File

@ -16,7 +16,12 @@ import { Dayjs } from "dayjs";
import _ from "lodash";
import { GLOBAL_INSTANCE_NAME } from "../../../config/const";
import { dayjsToTimestamp, timestampToDayjs } from "../../../tools/time";
import { useCmdAssistantDialog, usePortEditDialog, useVolumeEditDialog } from "@/components/fc";
import {
useCmdAssistantDialog,
useDockerEnvEditDialog,
usePortEditDialog,
useVolumeEditDialog
} from "@/components/fc";
import { dockerPortsArray } from "@/tools/common";
interface FormDetail extends InstanceDetail {
@ -69,12 +74,14 @@ const isGlobalTerminal = computed(() => {
const loadImages = async () => {
try {
dockerImages.value = ["TEST"];
const images = await getImageList({
params: {
daemonId: props.daemonId ?? ""
},
method: "GET"
});
if (images.value) {
dockerImages.value = [t("TXT_CODE_3362d4b7")];
for (const iterator of images.value) {
@ -169,7 +176,7 @@ const openCmdAssistDialog = async () => {
const cmd = await useCmdAssistantDialog();
if (options.value && cmd) options.value.config.startCommand = cmd;
};
const handleEditDockerConfig = async (type: "port" | "volume") => {
const handleEditDockerConfig = async (type: "port" | "volume" | "env") => {
if (type === "port" && options.value?.config) {
// "25565:25565/tcp 8080:8080/tcp" -> Array
const portArray = dockerPortsArray(options.value?.config.docker.ports || []);
@ -190,6 +197,19 @@ const handleEditDockerConfig = async (type: "port" | "volume") => {
const volumesArray = result.map((v) => `${v.host}:${v.container}`);
options.value.config.docker.extraVolumes = volumesArray;
}
if (type === "env" && options.value?.config) {
const envs = options.value.config.docker.env?.map((v) => {
const tmp = v.split("=");
return {
label: tmp[0] || "",
value: tmp[1] || ""
};
});
const result = await useDockerEnvEditDialog(envs);
const envsArray = result.map((v) => `${v.label}=${v.value}`);
options.value.config.docker.env = envsArray;
}
};
defineExpose({
@ -218,6 +238,7 @@ defineExpose({
autocomplete="off"
>
<a-row :gutter="20">
AAAAA: {{ options?.config.docker.env }}
<a-col :xs="24" :md="12" :offset="0">
<a-form-item name="nickname">
<a-typography-title :level="5" class="require-field">
@ -424,6 +445,21 @@ defineExpose({
</a-input-group>
</a-form-item>
</a-col>
<a-col :xs="24" :lg="8" :offset="0">
<a-form-item>
<a-typography-title :level="5">{{ t("环境变量") }}</a-typography-title>
<a-typography-paragraph>
<a-typography-text type="secondary">
{{ t("为容器传递环境变量") }}
</a-typography-text>
</a-typography-paragraph>
<a-input-group compact>
<a-button type="default" @click="() => handleEditDockerConfig('env')">
{{ t("TXT_CODE_ad207008") }}
</a-button>
</a-input-group>
</a-form-item>
</a-col>
<a-col :xs="24" :lg="8" :offset="0">
<a-form-item>
<a-typography-title :level="5">{{ t("TXT_CODE_3e68ca00") }}</a-typography-title>