fix(backups): default ignore-inode for fuse volumes
This commit is contained in:
parent
35dd49d7ac
commit
d19d827496
3 changed files with 96 additions and 1 deletions
|
|
@ -311,6 +311,94 @@ describe("backup execution - validation failures", () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("adds ignore-inode by default for FUSE-backed volumes", async () => {
|
||||
const { runBackupMock } = setup();
|
||||
const volume = await createTestVolume({
|
||||
type: "sftp",
|
||||
config: {
|
||||
backend: "sftp",
|
||||
host: "storage.example.com",
|
||||
port: 22,
|
||||
username: "backup",
|
||||
privateKey: "key",
|
||||
path: "/data",
|
||||
skipHostKeyCheck: false,
|
||||
},
|
||||
});
|
||||
const repository = await createTestRepository();
|
||||
const schedule = await createTestBackupSchedule({
|
||||
volumeId: volume.id,
|
||||
repositoryId: repository.id,
|
||||
});
|
||||
|
||||
await backupsService.executeBackup(schedule.id);
|
||||
|
||||
expect(runBackupMock).toHaveBeenCalledWith(
|
||||
"local",
|
||||
expect.objectContaining({
|
||||
payload: expect.objectContaining({
|
||||
options: expect.objectContaining({
|
||||
customResticParams: ["--ignore-inode"],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("does not add ignore-inode by default for directory volumes", async () => {
|
||||
const { runBackupMock } = setup();
|
||||
const volume = await createTestVolume();
|
||||
const repository = await createTestRepository();
|
||||
const schedule = await createTestBackupSchedule({
|
||||
volumeId: volume.id,
|
||||
repositoryId: repository.id,
|
||||
});
|
||||
|
||||
await backupsService.executeBackup(schedule.id);
|
||||
|
||||
expect(runBackupMock).toHaveBeenCalledWith(
|
||||
"local",
|
||||
expect.objectContaining({
|
||||
payload: expect.objectContaining({
|
||||
options: expect.objectContaining({
|
||||
customResticParams: [],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("does not duplicate ignore-inode when already configured", async () => {
|
||||
const { runBackupMock } = setup();
|
||||
const volume = await createTestVolume({
|
||||
type: "rclone",
|
||||
config: {
|
||||
backend: "rclone",
|
||||
remote: "remote",
|
||||
path: "/data",
|
||||
},
|
||||
});
|
||||
const repository = await createTestRepository();
|
||||
const schedule = await createTestBackupSchedule({
|
||||
volumeId: volume.id,
|
||||
repositoryId: repository.id,
|
||||
customResticParams: ["--ignore-inode"],
|
||||
});
|
||||
|
||||
await backupsService.executeBackup(schedule.id);
|
||||
|
||||
expect(runBackupMock).toHaveBeenCalledWith(
|
||||
"local",
|
||||
expect.objectContaining({
|
||||
payload: expect.objectContaining({
|
||||
options: expect.objectContaining({
|
||||
customResticParams: ["--ignore-inode"],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("should fail backup when the local agent is unavailable", async () => {
|
||||
const { runBackupMock } = setup();
|
||||
const volume = await createTestVolume();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import { createBackupOptions } from "./backup.helpers";
|
|||
import { toErrorDetails } from "../../utils/errors";
|
||||
|
||||
const LOCAL_AGENT_ID = "local";
|
||||
const FUSE_VOLUME_BACKENDS = new Set<Volume["type"]>(["rclone", "sftp", "webdav"]);
|
||||
const IGNORE_INODE_FLAG = "--ignore-inode";
|
||||
|
||||
type BackupExecutionRequest = {
|
||||
scheduleId: number;
|
||||
|
|
@ -33,6 +35,11 @@ const createBackupRunPayload = async ({
|
|||
}: BackupExecutionRequest & { jobId: string }): Promise<BackupRunPayload> => {
|
||||
const sourcePath = getVolumePath(volume);
|
||||
const { signal: _, ...options } = createBackupOptions(schedule, sourcePath);
|
||||
|
||||
if (FUSE_VOLUME_BACKENDS.has(volume.type) && !options.customResticParams.includes(IGNORE_INODE_FLAG)) {
|
||||
options.customResticParams = [...options.customResticParams, IGNORE_INODE_FLAG];
|
||||
}
|
||||
|
||||
const repositoryConfig = await decryptRepositoryConfig(repository.config);
|
||||
const encryptedResticPassword = await resticDeps.getOrganizationResticPassword(organizationId);
|
||||
const resticPassword = await resticDeps.resolveSecret(encryptedResticPassword);
|
||||
|
|
|
|||
|
|
@ -68,5 +68,5 @@ export const createBackupOptions = (schedule: BackupSchedule, volumePath: string
|
|||
includePatterns: schedule.includePatterns
|
||||
? schedule.includePatterns.map((p) => processPattern(p, volumePath, true))
|
||||
: undefined,
|
||||
customResticParams: schedule.customResticParams ?? undefined,
|
||||
customResticParams: schedule.customResticParams ?? [],
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue