ajusta lógica de backup incremental e melhora documentação do método getSelfBindSource
This commit is contained in:
parent
b66ebda525
commit
01dd302b97
|
|
@ -445,20 +445,11 @@ class BackupService {
|
||||||
} else {
|
} else {
|
||||||
// Fallback para containers sem GNU tar: usa helper container com GNU tar
|
// Fallback para containers sem GNU tar: usa helper container com GNU tar
|
||||||
// montando os volumes diretamente — produz .snar como qualquer outro container.
|
// montando os volumes diretamente — produz .snar como qualquer outro container.
|
||||||
if (runMode === 'incremental') {
|
if (runMode === 'full') {
|
||||||
try {
|
// Full: remove .snar anterior para forçar snapshot limpo no helper.
|
||||||
await fs.access(absoluteSnapshotPath);
|
|
||||||
tarIncrementalFlag = `--listed-incremental=${shellQuote('/backuproot/' + snapshotRelativePath)}`;
|
|
||||||
pushLog('Backup incremental via helper: snapshot anterior encontrado.', 'preparando');
|
|
||||||
} catch {
|
|
||||||
pushLog('Aviso: snapshot anterior nao encontrado, gerando backup completo via helper.', 'preparando');
|
|
||||||
tarIncrementalFlag = `--listed-incremental=${shellQuote('/backuproot/' + snapshotRelativePath)}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Full: remove .snar anterior para forçar snapshot limpo.
|
|
||||||
await fs.rm(absoluteSnapshotPath, { force: true }).catch(() => null);
|
await fs.rm(absoluteSnapshotPath, { force: true }).catch(() => null);
|
||||||
tarIncrementalFlag = `--listed-incremental=${shellQuote('/backuproot/' + snapshotRelativePath)}`;
|
|
||||||
}
|
}
|
||||||
|
// O .snar é gerenciado pelo helper usando helperSnarPath calculado abaixo.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Containers BusyBox sem GNU tar: roda o tar num helper container que TEM GNU tar,
|
// Containers BusyBox sem GNU tar: roda o tar num helper container que TEM GNU tar,
|
||||||
|
|
@ -469,28 +460,31 @@ class BackupService {
|
||||||
|
|
||||||
// Quando rodando dentro do Docker, backupRoot é um path interno do container da app.
|
// Quando rodando dentro do Docker, backupRoot é um path interno do container da app.
|
||||||
// Precisamos do source real (host path ou volume name) para passar ao helper container.
|
// Precisamos do source real (host path ou volume name) para passar ao helper container.
|
||||||
const backupRootSource = await this.dockerService.getSelfBindSource(backupRoot);
|
const selfBind = await this.dockerService.getSelfBindSource(backupRoot);
|
||||||
if (!backupRootSource) {
|
if (!selfBind) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Nao foi possivel determinar o source do diretorio de backup (${backupRoot}) para o helper. ` +
|
`Nao foi possivel determinar o source do diretorio de backup (${backupRoot}) para o helper. ` +
|
||||||
'Verifique se o diretorio de backup esta montado via volume ou bind no container da app.'
|
'Verifique se o diretorio de backup esta montado via volume ou bind no container da app.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const helperBinds = [`${backupRootSource}:/backuproot`];
|
// suffix é o subpath dentro do volume (ex: /backups quando mount=/app/data e backupRoot=/app/data/backups)
|
||||||
|
const helperBackupRoot = `/backuproot_base${selfBind.suffix}`;
|
||||||
|
const helperBinds = [`${selfBind.source}:/backuproot_base`];
|
||||||
const helperRelPaths = [];
|
const helperRelPaths = [];
|
||||||
for (const [index, mount] of activeMounts.entries()) {
|
for (const [index, mount] of activeMounts.entries()) {
|
||||||
const src = mount.type === 'volume' ? mount.name : mount.source;
|
const src = mount.type === 'volume' ? mount.name : mount.source;
|
||||||
helperBinds.push(`${src}:/payload/m${index}:ro`);
|
helperBinds.push(`${src}:/payload/m${index}:ro`);
|
||||||
helperRelPaths.push(`payload/m${index}`);
|
helperRelPaths.push(`payload/m${index}`);
|
||||||
}
|
}
|
||||||
const helperArchivePath = `/backuproot/${archiveRelativePath}`;
|
const helperArchivePath = `${helperBackupRoot}/${archiveRelativePath}`;
|
||||||
const helperSnarDir = path.posix.dirname(`/backuproot/${snapshotRelativePath}`);
|
const helperSnarPath = `${helperBackupRoot}/${snapshotRelativePath}`;
|
||||||
|
const helperSnarDir = path.posix.dirname(helperSnarPath);
|
||||||
const helperCmd = [
|
const helperCmd = [
|
||||||
'set -u',
|
'set -u',
|
||||||
`mkdir -p ${shellQuote(path.posix.dirname(helperArchivePath))} ${shellQuote(helperSnarDir)}`,
|
`mkdir -p ${shellQuote(path.posix.dirname(helperArchivePath))} ${shellQuote(helperSnarDir)}`,
|
||||||
`echo "__DBKP_TAR_BEGIN__" 1>&2`,
|
`echo "__DBKP_TAR_BEGIN__" 1>&2`,
|
||||||
`tar --ignore-failed-read ${tarIncrementalFlag} -czvf ${shellQuote(helperArchivePath)} -C / ${helperRelPaths.map((p) => shellQuote(p)).join(' ')}; TAR_RC=$?; [ $TAR_RC -le 1 ] || exit $TAR_RC`,
|
`tar --ignore-failed-read --listed-incremental=${shellQuote(helperSnarPath)} -czvf ${shellQuote(helperArchivePath)} -C / ${helperRelPaths.map((p) => shellQuote(p)).join(' ')}; TAR_RC=$?; [ $TAR_RC -le 1 ] || exit $TAR_RC`,
|
||||||
].join('; ');
|
].join('; ');
|
||||||
|
|
||||||
await this.dockerService.runHelper({
|
await this.dockerService.runHelper({
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,9 @@ class DockerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dado um caminho absoluto dentro do container da app (ex: /app/data/backups),
|
// Dado um caminho absoluto dentro do container da app (ex: /app/data/backups),
|
||||||
// retorna o source do bind/volume que o cobre (para usar em helper binds).
|
// retorna { source, suffix } onde source é o bind/volume que cobre o caminho
|
||||||
// Se não encontrar mount correspondente, retorna null.
|
// e suffix é o subpath dentro desse mount (ex: source='data_vol', suffix='/backups').
|
||||||
|
// Retorna null se não encontrar mount correspondente.
|
||||||
async getSelfBindSource(containerPath) {
|
async getSelfBindSource(containerPath) {
|
||||||
const mounts = await this._getSelfMounts();
|
const mounts = await this._getSelfMounts();
|
||||||
const normalized = containerPath.replace(/\/+$/, '');
|
const normalized = containerPath.replace(/\/+$/, '');
|
||||||
|
|
@ -100,7 +101,9 @@ class DockerService {
|
||||||
for (const mount of sorted) {
|
for (const mount of sorted) {
|
||||||
const dest = (mount.Destination || '').replace(/\/+$/, '');
|
const dest = (mount.Destination || '').replace(/\/+$/, '');
|
||||||
if (normalized === dest || normalized.startsWith(dest + '/')) {
|
if (normalized === dest || normalized.startsWith(dest + '/')) {
|
||||||
return mount.Type === 'volume' ? mount.Name : mount.Source;
|
const source = mount.Type === 'volume' ? mount.Name : mount.Source;
|
||||||
|
const suffix = normalized.slice(dest.length) || '';
|
||||||
|
return { source, suffix };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue