fix: write the dump to a temporary file first
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result, anyhow, bail};
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::{self, File},
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
@@ -157,6 +157,13 @@ fn restore_cluster(db: &Database, script: &str, file: &Path) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn suffixed(path: &Path, suffix: &str) -> PathBuf {
|
||||||
|
let mut out = path.as_os_str().to_owned();
|
||||||
|
out.push(suffix);
|
||||||
|
|
||||||
|
PathBuf::from(out)
|
||||||
|
}
|
||||||
|
|
||||||
fn read_header(path: &Path) -> Result<Vec<u8>> {
|
fn read_header(path: &Path) -> Result<Vec<u8>> {
|
||||||
let mut file = File::open(path).with_context(|| format!("opening {}", path.display()))?;
|
let mut file = File::open(path).with_context(|| format!("opening {}", path.display()))?;
|
||||||
let mut header = vec![0; HEADER_LEN];
|
let mut header = vec![0; HEADER_LEN];
|
||||||
@@ -343,15 +350,21 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
|||||||
|
|
||||||
eprintln!("dumping to local file {}", file.to_string_lossy());
|
eprintln!("dumping to local file {}", file.to_string_lossy());
|
||||||
|
|
||||||
let stdout = Stdio::from(File::create(file)?);
|
// written beside the target and renamed once the dump succeeds, so a failure
|
||||||
if gzip {
|
// cannot destroy the dump that is already there
|
||||||
|
let partial = suffixed(file, ".partial");
|
||||||
|
let stdout = Stdio::from(
|
||||||
|
File::create(&partial).with_context(|| format!("creating {}", partial.display()))?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let dumped = if gzip {
|
||||||
// the whole pipeline has to arrive as one shell argument
|
// the whole pipeline has to arrive as one shell argument
|
||||||
CommandBuilder::docker()
|
CommandBuilder::docker()
|
||||||
.args("exec")
|
.args("exec")
|
||||||
.arg(&db.container)
|
.arg(&db.container)
|
||||||
.args("sh -c")
|
.args("sh -c")
|
||||||
.arg(format!("{} | gzip", dump_command(&db, format)))
|
.arg(format!("{} | gzip", dump_command(&db, format)))
|
||||||
.exec_redirect_stdout(stdout)?;
|
.exec_redirect_stdout(stdout)
|
||||||
} else {
|
} else {
|
||||||
let dumping = match format.flag() {
|
let dumping = match format.flag() {
|
||||||
Some(flag) => in_container(&db)
|
Some(flag) => in_container(&db)
|
||||||
@@ -362,9 +375,17 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
|||||||
None => in_container(&db).args("pg_dumpall -U").arg(&db.user),
|
None => in_container(&db).args("pg_dumpall -U").arg(&db.user),
|
||||||
};
|
};
|
||||||
|
|
||||||
dumping.exec_redirect_stdout(stdout)?;
|
dumping.exec_redirect_stdout(stdout)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = dumped {
|
||||||
|
let _ = fs::remove_file(&partial);
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::rename(&partial, file)
|
||||||
|
.with_context(|| format!("renaming {} to {}", partial.display(), file.display()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user