Adiciona suporte para detecção de GNU tar no DockerService e ajusta gerenciamento de snapshots no BackupService
This commit is contained in:
parent
a8036507bb
commit
52bec257ee
|
|
@ -418,19 +418,41 @@ class BackupService {
|
||||||
const snarInContainer = containerSnapshotPath(profile.id, containerId, backupScope);
|
const snarInContainer = containerSnapshotPath(profile.id, containerId, backupScope);
|
||||||
const absoluteSnapshotPath = path.posix.join(backupRoot, snapshotRelativePath);
|
const absoluteSnapshotPath = path.posix.join(backupRoot, snapshotRelativePath);
|
||||||
|
|
||||||
// Gerencia o .snar assim como o script shell usa --listed-incremental=$dirbackup/backup.snar:
|
// Detecta se o container tem GNU tar (--listed-incremental é extensão GNU).
|
||||||
// - Full: remove o .snar anterior do container para forçar snapshot limpo.
|
// Containers Alpine/BusyBox usam o fallback --newer-mtime.
|
||||||
// - Incremental: injeta o .snar salvo no diretório de backup de volta no container.
|
const hasGnuTar = await this.dockerService.containerHasGnuTar(containerId);
|
||||||
if (runMode === 'full') {
|
pushLog(`GNU tar detectado no container: ${hasGnuTar ? 'sim' : 'nao (usando --newer-mtime como fallback)'}.`, 'preparando');
|
||||||
await this.dockerService.runContainerCommand(containerId, `rm -f ${shellQuote(snarInContainer)}`).catch(() => null);
|
|
||||||
pushLog('Backup full: snapshot incremental anterior removido.', 'preparando');
|
let tarIncrementalFlag = '';
|
||||||
|
|
||||||
|
if (hasGnuTar) {
|
||||||
|
// Gerencia o .snar assim como o script shell usa --listed-incremental=$dirbackup/backup.snar:
|
||||||
|
// - Full: remove o .snar anterior do container para forçar snapshot limpo.
|
||||||
|
// - Incremental: injeta o .snar salvo no diretório de backup de volta no container.
|
||||||
|
if (runMode === 'full') {
|
||||||
|
await this.dockerService.runContainerCommand(containerId, `rm -f ${shellQuote(snarInContainer)}`).catch(() => null);
|
||||||
|
pushLog('Backup full: snapshot incremental anterior removido.', 'preparando');
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await fs.access(absoluteSnapshotPath);
|
||||||
|
await this.dockerService.putSnarToContainer(containerId, absoluteSnapshotPath, snarInContainer);
|
||||||
|
pushLog('Backup incremental: snapshot anterior restaurado no container.', 'preparando');
|
||||||
|
} catch {
|
||||||
|
pushLog('Aviso: snapshot anterior nao encontrado, gerando backup completo.', 'preparando');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tarIncrementalFlag = `--listed-incremental=${shellQuote(snarInContainer)}`;
|
||||||
} else {
|
} else {
|
||||||
try {
|
// Fallback para containers sem GNU tar: --newer-mtime baseado no timestamp do último backup.
|
||||||
await fs.access(absoluteSnapshotPath);
|
if (runMode === 'incremental') {
|
||||||
await this.dockerService.putSnarToContainer(containerId, absoluteSnapshotPath, snarInContainer);
|
const lastTime = await this.store.getLastContainerBackupTime(profile.id, containerId);
|
||||||
pushLog('Backup incremental: snapshot anterior restaurado no container.', 'preparando');
|
if (lastTime) {
|
||||||
} catch {
|
const unixSec = Math.floor(new Date(lastTime).getTime() / 1000);
|
||||||
pushLog('Aviso: snapshot anterior nao encontrado, gerando backup completo.', 'preparando');
|
tarIncrementalFlag = `--newer-mtime=@${unixSec}`;
|
||||||
|
pushLog(`Backup incremental (--newer-mtime): arquivos modificados apos ${lastTime}.`, 'preparando');
|
||||||
|
} else {
|
||||||
|
pushLog('Aviso: sem backup anterior, gerando full.', 'preparando');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,11 +464,11 @@ class BackupService {
|
||||||
|
|
||||||
if (backupScope === 'container') {
|
if (backupScope === 'container') {
|
||||||
tarParts.push(
|
tarParts.push(
|
||||||
`tar --warning=no-file-changed --ignore-failed-read --listed-incremental=${shellQuote(snarInContainer)} -czvf - -C / --exclude=proc --exclude=sys --exclude=dev --exclude=run --exclude=tmp .`
|
`tar --warning=no-file-changed --ignore-failed-read ${tarIncrementalFlag} -czvf - -C / --exclude=proc --exclude=sys --exclude=dev --exclude=run --exclude=tmp .`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
tarParts.push(
|
tarParts.push(
|
||||||
`tar --warning=no-file-changed --ignore-failed-read --listed-incremental=${shellQuote(snarInContainer)} -czvf - -C / ${relSourcePaths.map((item) => shellQuote(item)).join(' ')}`
|
`tar --warning=no-file-changed --ignore-failed-read ${tarIncrementalFlag} -czvf - -C / ${relSourcePaths.map((item) => shellQuote(item)).join(' ')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,9 +495,11 @@ class BackupService {
|
||||||
|
|
||||||
// Persiste o .snar atualizado no diretório de backup (como o script shell faz com $dirbackup/backup.snar)
|
// Persiste o .snar atualizado no diretório de backup (como o script shell faz com $dirbackup/backup.snar)
|
||||||
// para que a cadeia incremental sobreviva a recriações do container.
|
// para que a cadeia incremental sobreviva a recriações do container.
|
||||||
const snarSaved = await this.dockerService.getSnarFromContainer(containerId, snarInContainer, absoluteSnapshotPath).catch(() => false);
|
if (hasGnuTar) {
|
||||||
if (snarSaved) {
|
const snarSaved = await this.dockerService.getSnarFromContainer(containerId, snarInContainer, absoluteSnapshotPath).catch(() => false);
|
||||||
pushLog('Snapshot incremental salvo no diretorio de backup.', 'finalizando');
|
if (snarSaved) {
|
||||||
|
pushLog('Snapshot incremental salvo no diretorio de backup.', 'finalizando');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (tempStarted) {
|
if (tempStarted) {
|
||||||
|
|
|
||||||
|
|
@ -437,6 +437,17 @@ class DockerService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retorna true se o container tem GNU tar (suporta --listed-incremental).
|
||||||
|
// Containers Alpine/BusyBox retornam false e devem usar --newer-mtime como fallback.
|
||||||
|
async containerHasGnuTar(containerId) {
|
||||||
|
try {
|
||||||
|
const output = await this.runContainerCommand(containerId, 'tar --version 2>/dev/null | head -1');
|
||||||
|
return /GNU tar/i.test(output);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Injeta um arquivo .snar local no container no caminho absoluto informado.
|
// Injeta um arquivo .snar local no container no caminho absoluto informado.
|
||||||
// Usa tar POSIX em memória, sem depender do tar do sistema.
|
// Usa tar POSIX em memória, sem depender do tar do sistema.
|
||||||
async putSnarToContainer(containerId, localSnarPath, containerSnarPath) {
|
async putSnarToContainer(containerId, localSnarPath, containerSnarPath) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue