Skip to content

Commit 0986ba8

Browse files
authored
Replace failpoint in restore with our own version
It only tests one thing so far, restoring directories, but it seems easiest to just configure it in the restore options.
1 parent ce66658 commit 0986ba8

File tree

5 files changed

+100
-116
lines changed

5 files changed

+100
-116
lines changed

Cargo.lock

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ cachedir = "0.3"
4141
clicolors-control = "1.0"
4242
crc32c = { version = "0.6.6", optional = true }
4343
derive_more = "0.99"
44-
fail = { version = "0.5.1" }
4544
filetime = "0.2"
4645
futures = { version = "0.3", optional = true }
4746
globset = "0.4.5"
@@ -112,7 +111,3 @@ tracing-test = { version = "0.2", features = ["no-env-filter"] }
112111

113112
[profile.release]
114113
debug = true
115-
116-
[[test]]
117-
name = "failpoints"
118-
required-features = ["fail/failpoints"]

src/apath.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use serde::{Deserialize, Serialize};
3636
/// string ordering.
3737
///
3838
/// Apaths must start with `/` and not end with `/` unless they have length 1.
39-
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
39+
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
4040
pub struct Apath(String);
4141

4242
impl Apath {
@@ -348,15 +348,21 @@ mod test {
348348
assert!(Apath::from("/").is_prefix_of(&Apath::from("/stuff")));
349349
assert!(Apath::from("/").is_prefix_of(&Apath::from("/")));
350350
assert!(Apath::from("/stuff").is_prefix_of(&Apath::from("/stuff/file")));
351-
assert!(Apath::from("/stuff/file")
352-
.is_prefix_of(&Apath::from("/stuff"))
353-
.not());
354-
assert!(Apath::from("/this")
355-
.is_prefix_of(&Apath::from("/that"))
356-
.not());
357-
assert!(Apath::from("/this")
358-
.is_prefix_of(&Apath::from("/that/other"))
359-
.not());
351+
assert!(
352+
Apath::from("/stuff/file")
353+
.is_prefix_of(&Apath::from("/stuff"))
354+
.not()
355+
);
356+
assert!(
357+
Apath::from("/this")
358+
.is_prefix_of(&Apath::from("/that"))
359+
.not()
360+
);
361+
assert!(
362+
Apath::from("/this")
363+
.is_prefix_of(&Apath::from("/that/other"))
364+
.not()
365+
);
360366
}
361367

362368
#[test]

src/restore.rs

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@
1212

1313
//! Restore from the archive to the filesystem.
1414
15-
use std::fs::{create_dir_all, File};
15+
#[cfg(test)]
16+
use std::collections::HashMap;
17+
use std::fs::{File, create_dir_all};
1618
use std::io::{self, Write};
1719
use std::path::{Path, PathBuf};
1820
use std::sync::Arc;
1921

20-
use fail::fail_point;
2122
use filetime::set_file_handle_times;
2223
#[cfg(unix)]
2324
use filetime::set_symlink_file_times;
@@ -33,17 +34,25 @@ use crate::unix_time::ToFileTime;
3334
use crate::*;
3435

3536
/// Description of how to restore a tree.
36-
// #[derive(Debug)]
3737
pub struct RestoreOptions {
38+
/// Exclude these paths from being restored.
3839
pub exclude: Exclude,
40+
3941
/// Restore only this subdirectory.
4042
pub only_subtree: Option<Apath>,
43+
44+
/// Overwrite existing files in the destination.
4145
pub overwrite: bool,
42-
// The band to select, or by default the last complete one.
46+
47+
/// The band to select, or by default the last complete one.
4348
pub band_selection: BandSelectionPolicy,
4449

45-
// Call this callback as each entry is successfully restored.
50+
/// Call this callback as each entry is successfully restored.
4651
pub change_callback: Option<ChangeCallback>,
52+
53+
/// For testing, fail to restore the named entries, with the given error.
54+
#[cfg(test)]
55+
pub inject_failures: HashMap<Apath, io::ErrorKind>,
4756
}
4857

4958
impl Default for RestoreOptions {
@@ -54,6 +63,8 @@ impl Default for RestoreOptions {
5463
exclude: Exclude::nothing(),
5564
only_subtree: None,
5665
change_callback: None,
66+
#[cfg(test)]
67+
inject_failures: HashMap::new(),
5768
}
5869
}
5970
}
@@ -98,14 +109,13 @@ pub async fn restore(
98109
Kind::Dir => {
99110
monitor.count(Counter::Dirs, 1);
100111
if *entry.apath() != Apath::root() {
101-
if let Err(err) = create_dir(&path) {
102-
if err.kind() != io::ErrorKind::AlreadyExists {
103-
monitor.error(Error::RestoreDirectory {
104-
path: path.clone(),
105-
source: err,
106-
});
107-
continue;
108-
}
112+
// Create all the parents in case we're restoring only a nested subtree.
113+
if let Err(err) = restore_dir(&entry.apath, &path, &options) {
114+
monitor.error(Error::RestoreDirectory {
115+
path: path.clone(),
116+
source: err,
117+
});
118+
continue;
109119
}
110120
}
111121
deferrals.push(DirDeferral {
@@ -146,15 +156,22 @@ pub async fn restore(
146156
Ok(())
147157
}
148158

149-
fn create_dir(path: &Path) -> io::Result<()> {
150-
fail_point!("restore::create-dir", |_| {
151-
Err(io::Error::new(
152-
io::ErrorKind::PermissionDenied,
153-
"Simulated failure",
154-
))
155-
});
156-
// Create all the parents in case we're restoring only a nested subtree.
157-
create_dir_all(path)
159+
fn restore_dir(apath: &Apath, restore_path: &Path, options: &RestoreOptions) -> io::Result<()> {
160+
#[cfg(test)]
161+
{
162+
if let Some(err_kind) = options.inject_failures.get(apath) {
163+
return Err(io::Error::from(*err_kind));
164+
}
165+
}
166+
let _ = apath;
167+
let _ = options;
168+
create_dir_all(restore_path).or_else(|err| {
169+
if err.kind() == io::ErrorKind::AlreadyExists {
170+
Ok(())
171+
} else {
172+
Err(err)
173+
}
174+
})
158175
}
159176

160177
/// Recorded changes to apply to directories after all their contents
@@ -307,13 +324,16 @@ fn restore_symlink(_restore_path: &Path, entry: &IndexEntry) -> Result<()> {
307324
#[cfg(test)]
308325
mod test {
309326
use std::fs::{create_dir, write};
327+
use std::io;
328+
use std::path::Path;
310329
use std::sync::{Arc, Mutex};
311330

312331
use tempfile::TempDir;
313332

314333
use crate::counters::Counter;
315334
use crate::monitor::test::TestMonitor;
316-
use crate::test_fixtures::{store_two_versions, TreeFixture};
335+
use crate::test_fixtures::{TreeFixture, store_two_versions};
336+
use crate::transport::Transport;
317337
use crate::*;
318338

319339
#[tokio::test]
@@ -501,7 +521,7 @@ mod test {
501521
use std::fs::{read_link, symlink_metadata};
502522
use std::path::PathBuf;
503523

504-
use filetime::{set_symlink_file_times, FileTime};
524+
use filetime::{FileTime, set_symlink_file_times};
505525

506526
let af = Archive::create_temp().await;
507527
let srcdir = TreeFixture::new();
@@ -530,4 +550,44 @@ mod test {
530550
PathBuf::from("target")
531551
);
532552
}
553+
554+
#[tokio::test]
555+
async fn create_dir_permission_denied() {
556+
let archive = Archive::open(Transport::local(Path::new(
557+
"testdata/archive/simple/v0.6.10",
558+
)))
559+
.await
560+
.unwrap();
561+
562+
let mut restore_options = RestoreOptions::default();
563+
restore_options
564+
.inject_failures
565+
.insert(Apath::from("/subdir"), io::ErrorKind::PermissionDenied);
566+
let restore_tmp = TempDir::new().unwrap();
567+
let monitor = TestMonitor::arc();
568+
let stats = restore(
569+
&archive,
570+
restore_tmp.path(),
571+
restore_options,
572+
monitor.clone(),
573+
)
574+
.await
575+
.expect("Restore");
576+
dbg!(&stats);
577+
let errors = monitor.take_errors();
578+
dbg!(&errors);
579+
assert_eq!(errors.len(), 2);
580+
if let Error::RestoreDirectory { path, .. } = &errors[0] {
581+
assert!(path.ends_with("subdir"));
582+
} else {
583+
panic!("Unexpected error {:?}", errors[0]);
584+
}
585+
// Also, since we didn't create the directory, we fail to create the file within it.
586+
if let Error::RestoreFile { path, source } = &errors[1] {
587+
assert!(path.ends_with("subdir/subfile"));
588+
assert_eq!(source.kind(), io::ErrorKind::NotFound);
589+
} else {
590+
panic!("Unexpected error {:?}", errors[1]);
591+
}
592+
}
533593
}

tests/failpoints.rs

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)