mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
fix(mounts): use bun shell instead of execFile
This commit is contained in:
@@ -6,9 +6,8 @@ await Bun.build({
|
||||
sourcemap: true,
|
||||
minify: {
|
||||
whitespace: true,
|
||||
identifiers: true,
|
||||
identifiers: false,
|
||||
syntax: true,
|
||||
keepNames: true,
|
||||
},
|
||||
external: ["ssh2"],
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import { logger } from "../utils/logger";
|
||||
|
||||
export type SystemCapabilities = {
|
||||
docker: boolean;
|
||||
hostProc: boolean;
|
||||
};
|
||||
|
||||
let capabilitiesPromise: Promise<SystemCapabilities> | null = null;
|
||||
@@ -29,7 +28,6 @@ export async function getCapabilities(): Promise<SystemCapabilities> {
|
||||
async function detectCapabilities(): Promise<SystemCapabilities> {
|
||||
return {
|
||||
docker: await detectDocker(),
|
||||
hostProc: await detectHostProc(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,23 +53,3 @@ async function detectDocker(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if host proc is available by attempting to access /host/proc/1/ns/mnt
|
||||
* This allows using nsenter to execute mount commands in the host namespace
|
||||
*/
|
||||
async function detectHostProc(): Promise<boolean> {
|
||||
try {
|
||||
await fs.access("/host/proc/1/ns/mnt");
|
||||
|
||||
logger.info("Host proc capability: enabled");
|
||||
return true;
|
||||
} catch (_) {
|
||||
logger.warn(
|
||||
"Host proc capability: disabled. " +
|
||||
"To enable: mount /proc:/host/proc:ro in docker-compose.yml. " +
|
||||
"Mounts will be executed in container namespace instead of host namespace.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,14 @@
|
||||
import { execFile as execFileCb } from "node:child_process";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as npath from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { getCapabilities } from "../../../core/capabilities";
|
||||
import { OPERATION_TIMEOUT } from "../../../core/constants";
|
||||
import { toMessage } from "../../../utils/errors";
|
||||
import { logger } from "../../../utils/logger";
|
||||
|
||||
const execFile = promisify(execFileCb);
|
||||
import { $ } from "bun";
|
||||
|
||||
export const executeMount = async (args: string[]): Promise<void> => {
|
||||
const capabilities = await getCapabilities();
|
||||
let stderr: string | undefined;
|
||||
|
||||
if (capabilities.hostProc) {
|
||||
const result = await execFile("nsenter", ["--mount=/host/proc/1/ns/mnt", "mount", ...args], {
|
||||
timeout: OPERATION_TIMEOUT,
|
||||
maxBuffer: 1024 * 1024,
|
||||
});
|
||||
stderr = result.stderr;
|
||||
} else {
|
||||
const result = await execFile("mount", args, {
|
||||
timeout: OPERATION_TIMEOUT,
|
||||
maxBuffer: 1024 * 1024,
|
||||
});
|
||||
stderr = result.stderr;
|
||||
}
|
||||
const result = await $`mount ${args}`.nothrow();
|
||||
stderr = result.stderr.toString();
|
||||
|
||||
if (stderr?.trim()) {
|
||||
logger.warn(stderr.trim());
|
||||
@@ -33,22 +16,10 @@ export const executeMount = async (args: string[]): Promise<void> => {
|
||||
};
|
||||
|
||||
export const executeUnmount = async (path: string): Promise<void> => {
|
||||
const capabilities = await getCapabilities();
|
||||
let stderr: string | undefined;
|
||||
|
||||
if (capabilities.hostProc) {
|
||||
const result = await execFile("nsenter", ["--mount=/host/proc/1/ns/mnt", "umount", "-l", "-f", path], {
|
||||
timeout: OPERATION_TIMEOUT,
|
||||
maxBuffer: 1024 * 1024,
|
||||
});
|
||||
stderr = result.stderr;
|
||||
} else {
|
||||
const result = await execFile("umount", ["-l", "-f", path], {
|
||||
timeout: OPERATION_TIMEOUT,
|
||||
maxBuffer: 1024 * 1024,
|
||||
});
|
||||
stderr = result.stderr;
|
||||
}
|
||||
const result = await $`umount -l -f ${path}`.nothrow();
|
||||
stderr = result.stderr.toString();
|
||||
|
||||
if (stderr?.trim()) {
|
||||
logger.warn(stderr.trim());
|
||||
|
||||
@@ -189,7 +189,7 @@ const backup = async (
|
||||
|
||||
let stdout = "";
|
||||
|
||||
await safeSpawn({
|
||||
const res = await safeSpawn({
|
||||
command: "restic",
|
||||
args,
|
||||
env,
|
||||
@@ -210,6 +210,11 @@ const backup = async (
|
||||
},
|
||||
});
|
||||
|
||||
if (res.exitCode !== 0) {
|
||||
logger.error(`Restic backup failed: ${res.stderr}`);
|
||||
throw new Error(`Restic backup failed: ${res.stderr}`);
|
||||
}
|
||||
|
||||
const lastLine = stdout.trim();
|
||||
const resSummary = JSON.parse(lastLine ?? "{}");
|
||||
|
||||
|
||||
@@ -12,10 +12,19 @@ interface Params {
|
||||
finally?: () => Promise<void> | void;
|
||||
}
|
||||
|
||||
type SpawnResult = {
|
||||
exitCode: number;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
};
|
||||
|
||||
export const safeSpawn = (params: Params) => {
|
||||
const { command, args, env = {}, signal, ...callbacks } = params;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<SpawnResult>((resolve) => {
|
||||
let stdoutData = "";
|
||||
let stderrData = "";
|
||||
|
||||
const child = spawn(command, args, {
|
||||
env: { ...process.env, ...env },
|
||||
signal: signal,
|
||||
@@ -24,12 +33,16 @@ export const safeSpawn = (params: Params) => {
|
||||
child.stdout.on("data", (data) => {
|
||||
if (callbacks.onStdout) {
|
||||
callbacks.onStdout(data.toString());
|
||||
} else {
|
||||
stdoutData += data.toString();
|
||||
}
|
||||
});
|
||||
|
||||
child.stderr.on("data", (data) => {
|
||||
if (callbacks.onStderr) {
|
||||
callbacks.onStderr(data.toString());
|
||||
} else {
|
||||
stderrData += data.toString();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -40,7 +53,12 @@ export const safeSpawn = (params: Params) => {
|
||||
if (callbacks.finally) {
|
||||
await callbacks.finally();
|
||||
}
|
||||
reject(error);
|
||||
|
||||
resolve({
|
||||
exitCode: -1,
|
||||
stdout: stdoutData,
|
||||
stderr: stderrData,
|
||||
});
|
||||
});
|
||||
|
||||
child.on("close", async (code) => {
|
||||
@@ -50,7 +68,12 @@ export const safeSpawn = (params: Params) => {
|
||||
if (callbacks.finally) {
|
||||
await callbacks.finally();
|
||||
}
|
||||
resolve(code);
|
||||
|
||||
resolve({
|
||||
exitCode: code === null ? -1 : code,
|
||||
stdout: stdoutData,
|
||||
stderr: stderrData,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user