Skip to content

Commit 9577ea7

Browse files
authored
Merge branch 'MCSManager:master' into master
2 parents d61a1b6 + 5e3bf2c commit 9577ea7

32 files changed

Lines changed: 466 additions & 105 deletions

common/global.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ declare global {
110110
gpuDeviceIds?: string[];
111111
/** GPU driver name, default "nvidia" */
112112
gpuDriver?: string;
113+
/** Docker device read/write BPS. Format: /dev/sda:10MB */
114+
deviceReadBps?: string[];
115+
deviceWriteBps?: string[];
113116
}
114117

115118
interface IPanelResponseProtocol {

daemon/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

daemon/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mcsmanager-daemon",
3-
"version": "4.14.0",
3+
"version": "4.15.0",
44
"description": "Provides remote control capability for MCSManager to manage processes, scheduled tasks, I/O streams, and more",
55
"scripts": {
66
"dev": "nodemon --watch src --ext ts,js --exec \"npm run build && node --enable-source-maps production/app.js\"",
@@ -74,4 +74,4 @@
7474
"webpack-cli": "^4.10.0",
7575
"webpack-node-externals": "^3.0.0"
7676
}
77-
}
77+
}

daemon/src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const io = new Server(httpServer, {
9898
origin: "*",
9999
methods: ["GET", "POST", "PUT", "DELETE"]
100100
},
101-
maxHttpBufferSize: 1e8
101+
maxHttpBufferSize: 1e7
102102
});
103103

104104
// Initialize application instance system

daemon/src/common/compress.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export async function decompress(
7070
}
7171
};
7272

73-
if (!await check7zipStatus()) {
73+
if (await check7zipStatus()) {
7474
try {
7575
return await use7zip(zipPath, dest);
7676
} catch (error) {

daemon/src/entity/instance/Instance_config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ export default class InstanceConfig implements IGlobalInstanceConfig {
9090
gpuEnabled: false,
9191
gpuCount: -1,
9292
gpuDeviceIds: [],
93-
gpuDriver: "nvidia"
93+
gpuDriver: "nvidia",
94+
deviceReadBps: [],
95+
deviceWriteBps: []
9496
};
9597

9698
public pingConfig = {

daemon/src/entity/instance/instance.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ export default class Instance extends EventEmitter {
253253
configureEntityParams(this.config.docker, cfg.docker, "gpuCount", Number);
254254
configureEntityParams(this.config.docker, cfg.docker, "gpuDeviceIds");
255255
configureEntityParams(this.config.docker, cfg.docker, "gpuDriver", String);
256+
configureEntityParams(this.config.docker, cfg.docker, "deviceReadBps");
257+
configureEntityParams(this.config.docker, cfg.docker, "deviceWriteBps");
256258
}
257259
if (cfg.pingConfig) {
258260
configureEntityParams(this.config.pingConfig, cfg.pingConfig, "ip", String);

daemon/src/routers/Instance_router.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,23 @@ routerApp.on("instance/asynchronous", (ctx, data) => {
377377
const instance = InstanceSubsystem.getInstance(instanceUuid);
378378
const role = data.role as ROLE;
379379

380-
if (!role || !instance) {
381-
throw new Error("Invalid role or instance");
380+
if (!role) {
381+
throw new Error("Invalid role");
382+
}
383+
384+
// Quick install Minecraft server task
385+
// Why not use the ".execPreset("install", parameter)" that already exists in Instance?
386+
// Because the instance has not yet been created at this stage.
387+
if (taskName === "quick_install" && role === ROLE.ADMIN) {
388+
const newInstanceName = String(parameter.newInstanceName);
389+
const targetLink = String(parameter.targetLink);
390+
logger.info(`Quick install: Name: ${newInstanceName} | Download: ${targetLink}`);
391+
const task = createQuickInstallTask(targetLink, newInstanceName, parameter.setupInfo);
392+
return protocol.response(ctx, task.toObject());
393+
}
394+
395+
if (!instance) {
396+
throw new Error("Invalid instance");
382397
}
383398

384399
logger.info(
@@ -425,17 +440,6 @@ routerApp.on("instance/asynchronous", (ctx, data) => {
425440
return protocol.response(ctx, true);
426441
}
427442

428-
// Quick install Minecraft server task
429-
// Why not use the ".execPreset("install", parameter)" that already exists in Instance?
430-
// Because the instance has not yet been created at this stage.
431-
if (taskName === "quick_install" && role === ROLE.ADMIN) {
432-
const newInstanceName = String(parameter.newInstanceName);
433-
const targetLink = String(parameter.targetLink);
434-
logger.info(`Quick install: Name: ${newInstanceName} | Download: ${targetLink}`);
435-
const task = createQuickInstallTask(targetLink, newInstanceName, parameter.setupInfo);
436-
return protocol.response(ctx, task.toObject());
437-
}
438-
439443
throw new Error(`Access denied: ${taskName} is not allowed for role ${role}`);
440444
});
441445

daemon/src/routers/file_router.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ routerApp.on("file/download_from_url", async (ctx, data) => {
188188

189189
const fileManager = getFileManager(data.instanceUuid);
190190
fileManager.checkPath(fileName);
191+
fileManager.assertNotLink(fileName);
191192
const targetPath = fileManager.toAbsolutePath(fileName);
192193

193194
// Start download in background
@@ -224,6 +225,7 @@ routerApp.on("file/download_from_url_stop", (ctx, data) => {
224225
} else if (data.instanceUuid) {
225226
const fileManager = getFileManager(data.instanceUuid);
226227
fileManager.checkPath(data.fileName);
228+
fileManager.assertNotLink(data.fileName);
227229
const targetPath = fileManager.toAbsolutePath(data.fileName);
228230

229231
const result = downloadManager.stop(targetPath);

daemon/src/service/docker_process_service.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ export class SetupDockerContainer extends AsyncTask {
104104

105105
// example: 8080:8080/tcp
106106
if (publicAndPrivatePort.length == 2) {
107-
publicPortArray[`${publicAndPrivatePort[1]}/${protocol}`] = [
108-
{ HostPort: publicAndPrivatePort[0] }
109-
];
110-
exposedPorts[`${publicAndPrivatePort[1]}/${protocol}`] = {};
107+
const portKey = `${publicAndPrivatePort[1]}/${protocol}`;
108+
publicPortArray[portKey] ||= [];
109+
publicPortArray[portKey].push({ HostPort: publicAndPrivatePort[0] });
110+
exposedPorts[portKey] = {};
111111
logOpenedPorts.push({
112112
host: publicAndPrivatePort[0],
113113
container: Number(publicAndPrivatePort[1]),
@@ -118,10 +118,13 @@ export class SetupDockerContainer extends AsyncTask {
118118

119119
// example: 127.0.0.1:8080:8080/tcp
120120
if (publicAndPrivatePort.length == 3) {
121-
publicPortArray[`${publicAndPrivatePort[2]}/${protocol}`] = [
122-
{ HostIp: publicAndPrivatePort[0], HostPort: publicAndPrivatePort[1] }
123-
];
124-
exposedPorts[`${publicAndPrivatePort[2]}/${protocol}`] = {};
121+
const portKey = `${publicAndPrivatePort[2]}/${protocol}`;
122+
publicPortArray[portKey] ||= [];
123+
publicPortArray[portKey].push({
124+
HostIp: publicAndPrivatePort[0],
125+
HostPort: publicAndPrivatePort[1]
126+
});
127+
exposedPorts[portKey] = {};
125128
logOpenedPorts.push({
126129
host: publicAndPrivatePort[0] + ":" + publicAndPrivatePort[1],
127130
container: Number(publicAndPrivatePort[2]),
@@ -144,6 +147,22 @@ export class SetupDockerContainer extends AsyncTask {
144147
extraBinds.push({ hostPath, containerPath });
145148
}
146149

150+
const parseBlkioString = (input: string) => {
151+
const match = input.trim().match(/^([^:]+):(\d+)([KMG]?B?)$/i);
152+
if (!match) return null;
153+
const unit = (match[3] || "").charAt(0).toUpperCase();
154+
const multipliers: Record<string, number> = { K: 1024, M: 1024 ** 2, G: 1024 ** 3 };
155+
return { Path: match[1].trim(), Rate: parseInt(match[2]) * (multipliers[unit] || 1) };
156+
};
157+
158+
const deviceReadBps = (dockerConfig.deviceReadBps || [])
159+
.map(parseBlkioString)
160+
.filter((v) => v !== null);
161+
162+
const deviceWriteBps = (dockerConfig.deviceWriteBps || [])
163+
.map(parseBlkioString)
164+
.filter((v) => v !== null);
165+
147166
// memory limit
148167
let maxMemory: number | undefined = undefined;
149168
if (typeof dockerConfig.memory === "number" && dockerConfig.memory > 0)
@@ -403,6 +422,8 @@ export class SetupDockerContainer extends AsyncTask {
403422
"mcsmanager.instance.uuid": instance.instanceUuid
404423
},
405424
HostConfig: {
425+
BlkioDeviceReadBps: deviceReadBps.length > 0 ? deviceReadBps : undefined,
426+
BlkioDeviceWriteBps: deviceWriteBps.length > 0 ? deviceWriteBps : undefined,
406427
Memory: maxMemory,
407428
MemorySwap: memorySwap,
408429
MemorySwappiness: memorySwappiness,

0 commit comments

Comments
 (0)