@botlearn-course/daemon 0.0.20-beta.8 → 0.0.20-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,10 +7,14 @@ interface MountInfoEntry {
|
|
|
7
7
|
mountOptions: Set<string>;
|
|
8
8
|
superOptions: Set<string>;
|
|
9
9
|
}
|
|
10
|
+
export interface WorkspaceCapacityProbes {
|
|
11
|
+
statfsBytes(workspaceRoot: string): bigint;
|
|
12
|
+
backingFileBytes(imagePath: string): bigint;
|
|
13
|
+
}
|
|
10
14
|
export declare function parseWorkspaceMountInfo(text: string): MountInfoEntry[];
|
|
11
15
|
export declare function nasWorkspaceEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
12
16
|
export declare function workspaceFilesystemFlushCommand(env?: NodeJS.ProcessEnv): [string, string[]] | null;
|
|
13
17
|
export declare function flushWorkspaceFilesystem(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
14
18
|
export declare function isWorkspaceQuotaExceededError(error: unknown): boolean;
|
|
15
|
-
export declare function assertWorkspaceFilesystem(workspaceRoot: string, env?: NodeJS.ProcessEnv, mountInfo?: string): void;
|
|
19
|
+
export declare function assertWorkspaceFilesystem(workspaceRoot: string, env?: NodeJS.ProcessEnv, mountInfo?: string, capacityProbes?: WorkspaceCapacityProbes): void;
|
|
16
20
|
export {};
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { readFileSync, statfsSync } from "node:fs";
|
|
2
|
+
import { readFileSync, statfsSync, statSync } from "node:fs";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { assertRuntimeWorkspaceQuota } from "./workspace-quota.js";
|
|
5
5
|
export const WORKSPACE_IMAGE_FORMAT = "botlearn-workspace-image/1";
|
|
6
6
|
export const WORKSPACE_IMAGE_LOGICAL_BYTES = 3 * 1024 * 1024 * 1024;
|
|
7
7
|
const RUNTIME_LAUNCHER = "/opt/botlearn/bin/botlearn-runtime-launcher";
|
|
8
8
|
const execFileAsync = promisify(execFile);
|
|
9
|
+
const defaultCapacityProbes = {
|
|
10
|
+
statfsBytes(workspaceRoot) {
|
|
11
|
+
const filesystem = statfsSync(workspaceRoot, { bigint: true });
|
|
12
|
+
return filesystem.blocks * filesystem.bsize;
|
|
13
|
+
},
|
|
14
|
+
backingFileBytes(imagePath) {
|
|
15
|
+
return BigInt(statSync(imagePath).size);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
9
18
|
function decodeMountInfoPath(value) {
|
|
10
19
|
return value.replace(/\\([0-7]{3})/gu, (_match, octal) => String.fromCharCode(Number.parseInt(octal, 8)));
|
|
11
20
|
}
|
|
@@ -72,7 +81,7 @@ export function isWorkspaceQuotaExceededError(error) {
|
|
|
72
81
|
|| /\b(?:ENOSPC|EDQUOT|no space left on device|disk quota exceeded)\b/iu.test(detail)
|
|
73
82
|
|| (candidate.cause !== undefined && isWorkspaceQuotaExceededError(candidate.cause));
|
|
74
83
|
}
|
|
75
|
-
export function assertWorkspaceFilesystem(workspaceRoot, env = process.env, mountInfo) {
|
|
84
|
+
export function assertWorkspaceFilesystem(workspaceRoot, env = process.env, mountInfo, capacityProbes) {
|
|
76
85
|
if (!nasWorkspaceEnabled(env)) {
|
|
77
86
|
assertRuntimeWorkspaceQuota(workspaceRoot);
|
|
78
87
|
return;
|
|
@@ -96,10 +105,16 @@ export function assertWorkspaceFilesystem(workspaceRoot, env = process.env, moun
|
|
|
96
105
|
|| !mount.mountOptions.has("nosuid")) {
|
|
97
106
|
throw new Error("workspace_loop_mount_failed");
|
|
98
107
|
}
|
|
99
|
-
if (env.NODE_ENV === "test")
|
|
108
|
+
if (env.NODE_ENV === "test" && capacityProbes === undefined)
|
|
100
109
|
return;
|
|
101
|
-
const
|
|
102
|
-
|
|
110
|
+
const probes = capacityProbes ?? defaultCapacityProbes;
|
|
111
|
+
// fuse2fs reports a zero-sized statfs view in veFaaS even though the exact backing
|
|
112
|
+
// image is mounted and enforces the filesystem boundary. The mount proof above pins
|
|
113
|
+
// that image, so its validated file size is the authoritative capacity for FUSE.
|
|
114
|
+
const boundedBytes = mount.filesystemType === "fuse.ext4"
|
|
115
|
+
? probes.backingFileBytes(mount.source)
|
|
116
|
+
: probes.statfsBytes(workspaceRoot);
|
|
117
|
+
if (boundedBytes !== BigInt(WORKSPACE_IMAGE_LOGICAL_BYTES)) {
|
|
103
118
|
throw new Error("workspace_quota_mismatch");
|
|
104
119
|
}
|
|
105
120
|
}
|
package/package.json
CHANGED