chore: verbose logging for mount commands
Some checks failed
Release Workflow / checks (push) Has been cancelled
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled

This commit is contained in:
Nicolas Meienberger 2026-01-08 19:43:40 +01:00
parent 87403c9407
commit 05348e31b0
3 changed files with 48 additions and 21 deletions

View file

@ -10,8 +10,9 @@ ENV VITE_RESTIC_VERSION=${RESTIC_VERSION} \
VITE_RCLONE_VERSION=${RCLONE_VERSION} \
VITE_SHOUTRRR_VERSION=${SHOUTRRR_VERSION}
RUN apk upgrade --no-cache && \
apk add --no-cache davfs2=1.6.1-r2 openssh-client fuse3 sshfs tini nfs-utils cifs-utils
RUN apk update --no-cache && \
apk upgrade --no-cache && \
apk add --no-cache davfs2=1.6.1-r2 openssh-client fuse3 sshfs tini nfs-utils cifs-utils util-linux
ENTRYPOINT ["/sbin/tini", "-s", "--"]

View file

@ -1,5 +1,6 @@
import * as fs from "node:fs/promises";
import * as os from "node:os";
import { BACKEND_STATUS, type BackendConfig } from "~/schemas/volumes";
import { OPERATION_TIMEOUT } from "../../../core/constants";
import { toMessage } from "../../../utils/errors";
import { logger } from "../../../utils/logger";
@ -7,19 +8,24 @@ import { getMountForPath } from "../../../utils/mountinfo";
import { withTimeout } from "../../../utils/timeout";
import type { VolumeBackend } from "../backend";
import { executeMount, executeUnmount } from "../utils/backend-utils";
import { BACKEND_STATUS, type BackendConfig } from "~/schemas/volumes";
const mount = async (config: BackendConfig, path: string) => {
logger.debug(`Mounting volume ${path}...`);
if (config.backend !== "nfs") {
logger.error("Provided config is not for NFS backend");
return { status: BACKEND_STATUS.error, error: "Provided config is not for NFS backend" };
return {
status: BACKEND_STATUS.error,
error: "Provided config is not for NFS backend",
};
}
if (os.platform() !== "linux") {
logger.error("NFS mounting is only supported on Linux hosts.");
return { status: BACKEND_STATUS.error, error: "NFS mounting is only supported on Linux hosts." };
return {
status: BACKEND_STATUS.error,
error: "NFS mounting is only supported on Linux hosts.",
};
}
const { status } = await checkHealth(path);
@ -28,7 +34,9 @@ const mount = async (config: BackendConfig, path: string) => {
}
if (status === "error") {
logger.debug(`Trying to unmount any existing mounts at ${path} before mounting...`);
logger.debug(
`Trying to unmount any existing mounts at ${path} before mounting...`,
);
await unmount(path);
}
@ -36,7 +44,7 @@ const mount = async (config: BackendConfig, path: string) => {
await fs.mkdir(path, { recursive: true });
const source = `${config.server}:${config.exportPath}`;
const options = [`vers=${config.version}`, `port=${config.port}`];
const options = [`vers=${config.version}`, `port=${config.port}`, "nolock"];
if (config.readOnly) {
options.push("ro");
}
@ -62,7 +70,10 @@ const mount = async (config: BackendConfig, path: string) => {
const unmount = async (path: string) => {
if (os.platform() !== "linux") {
logger.error("NFS unmounting is only supported on Linux hosts.");
return { status: BACKEND_STATUS.error, error: "NFS unmounting is only supported on Linux hosts." };
return {
status: BACKEND_STATUS.error,
error: "NFS unmounting is only supported on Linux hosts.",
};
}
const run = async () => {
@ -83,7 +94,10 @@ const unmount = async (path: string) => {
try {
return await withTimeout(run(), OPERATION_TIMEOUT, "NFS unmount");
} catch (err) {
logger.error("Error unmounting NFS volume", { path, error: toMessage(err) });
logger.error("Error unmounting NFS volume", {
path,
error: toMessage(err),
});
return { status: BACKEND_STATUS.error, error: toMessage(err) };
}
};
@ -103,7 +117,9 @@ const checkHealth = async (path: string) => {
}
if (!mount.fstype.startsWith("nfs")) {
throw new Error(`Path ${path} is not mounted as NFS (found ${mount.fstype}).`);
throw new Error(
`Path ${path} is not mounted as NFS (found ${mount.fstype}).`,
);
}
logger.debug(`NFS volume at ${path} is healthy and mounted.`);
@ -121,7 +137,10 @@ const checkHealth = async (path: string) => {
}
};
export const makeNfsBackend = (config: BackendConfig, path: string): VolumeBackend => ({
export const makeNfsBackend = (
config: BackendConfig,
path: string,
): VolumeBackend => ({
mount: () => mount(config, path),
unmount: () => unmount(path),
checkHealth: () => checkHealth(path),

View file

@ -1,23 +1,30 @@
import * as fs from "node:fs/promises";
import * as npath from "node:path";
import { $ } from "bun";
import { toMessage } from "../../../utils/errors";
import { logger } from "../../../utils/logger";
import { $ } from "bun";
export const executeMount = async (args: string[]): Promise<void> => {
let stderr: string | undefined;
const shouldBeVerbose = process.env.LOG_LEVEL === "debug" || process.env.NODE_ENV !== "production";
const hasVerboseFlag = args.some((arg) => arg === "-v" || arg.startsWith("-vv"));
const effectiveArgs = shouldBeVerbose && !hasVerboseFlag ? ["-vvv", ...args] : args;
logger.debug(`Executing mount ${args.join(" ")}`);
const result = await $`mount ${args}`.nothrow();
stderr = result.stderr.toString();
logger.debug(`Executing mount ${effectiveArgs.join(" ")}`);
const result = await $`mount ${effectiveArgs}`.nothrow();
if (stderr?.trim()) {
logger.warn(stderr.trim());
const stdout = result.stdout.toString().trim();
const stderr = result.stderr.toString().trim();
if (result.exitCode === 0) {
logger.debug(stdout);
logger.debug(stderr);
return;
}
if (result.exitCode !== 0) {
throw new Error(`Mount command failed with exit code ${result.exitCode}: ${stderr?.trim()}`);
}
logger.warn(stdout);
logger.warn(stderr);
throw new Error(`Mount command failed with exit code ${result.exitCode}: ${stderr || stdout || "unknown error"}`);
};
export const executeUnmount = async (path: string): Promise<void> => {