Merge pull request #93 from rustic-rs/snapshot-path

Allow specifying snapshot with path for some commands
This commit is contained in:
aawsome 2022-07-30 13:30:20 +02:00 committed by GitHub
commit fcdb2ded5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 58 deletions

View File

@ -1,6 +1,6 @@
use std::fs::File;
use anyhow::Result;
use anyhow::{bail, Result};
use async_trait::async_trait;
use super::{FileType, Id, ReadBackend, WriteBackend};
@ -17,16 +17,13 @@ use ChooseBackend::{Local, Rclone, Rest};
impl ChooseBackend {
pub fn from_url(url: &str) -> Result<Self> {
if let Some(path) = url.strip_prefix("rclone:") {
return Ok(Rclone(RcloneBackend::new(path)?));
}
if let Some(path) = url.strip_prefix("rest:") {
return Ok(Rest(RestBackend::new(path)));
}
if let Some(path) = url.strip_prefix("local:") {
return Ok(Local(LocalBackend::new(path)));
}
Ok(Local(LocalBackend::new(url)))
Ok(match url.split_once(':') {
Some(("rclone", path)) => Rclone(RcloneBackend::new(path)?),
Some(("rest", path)) => Rest(RestBackend::new(path)),
Some(("local", path)) => Local(LocalBackend::new(path)),
Some((backend, _)) => bail!("backend {backend} is not supported!"),
None => Local(LocalBackend::new(url)),
})
}
}

View File

@ -1,6 +1,6 @@
use std::collections::{HashSet, VecDeque};
use std::mem;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use anyhow::{anyhow, Result};
@ -56,6 +56,24 @@ impl Tree {
Ok(serde_json::from_slice(&data)?)
}
pub async fn subtree_id(be: &impl IndexedBackend, mut id: Id, path: &Path) -> Result<Id> {
for p in path.iter() {
let p = p.to_str().unwrap();
// TODO: check for root instead
if p == "/" {
continue;
}
let tree = Tree::from_backend(be, id).await?;
let node = tree
.nodes()
.iter()
.find(|node| node.name() == p)
.ok_or_else(|| anyhow!("{} not found", p))?;
id = node.subtree().ok_or_else(|| anyhow!("{} is no dir", p))?;
}
Ok(id)
}
}
/// NodeStreamer recursively streams all nodes of a given tree including all subtrees in-order

View File

@ -1,6 +1,6 @@
use std::path::PathBuf;
use std::path::Path;
use anyhow::{anyhow, Result};
use anyhow::Result;
use clap::{Parser, Subcommand};
use indicatif::ProgressBar;
@ -36,11 +36,9 @@ struct IdOpt {
#[derive(Parser)]
struct TreeOpts {
/// snapshot id
id: String,
/// path within snapshot
path: PathBuf,
/// snapshot/path to restore
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
}
pub(super) async fn execute(be: &impl DecryptReadBackend, opts: Opts) -> Result<()> {
@ -76,25 +74,10 @@ async fn cat_blob(be: &impl DecryptReadBackend, tpe: BlobType, opt: IdOpt) -> Re
}
async fn cat_tree(be: &impl DecryptReadBackend, opts: TreeOpts) -> Result<()> {
let snap = SnapshotFile::from_str(be, &opts.id, |_| true, progress_counter()).await?;
let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, ""));
let snap = SnapshotFile::from_str(be, id, |_| true, progress_counter()).await?;
let index = IndexBackend::new(be, progress_counter()).await?;
let mut id = snap.tree;
for p in opts.path.iter() {
let p = p.to_str().unwrap();
// TODO: check for root instead
if p == "/" {
continue;
}
let tree = Tree::from_backend(&index, id).await?;
let node = tree
.nodes()
.iter()
.find(|node| node.name() == p)
.ok_or_else(|| anyhow!("{} not found", p))?;
id = node.subtree().ok_or_else(|| anyhow!("{} is no dir", p))?;
}
let id = Tree::subtree_id(&index, snap.tree, Path::new(path)).await?;
let data = index.blob_from_backend(&BlobType::Tree, &id).await?;
println!("{}", String::from_utf8(data)?);

View File

@ -1,3 +1,5 @@
use std::path::Path;
use anyhow::Result;
use clap::Parser;
use futures::StreamExt;
@ -5,30 +7,37 @@ use vlog::*;
use super::progress_counter;
use crate::backend::DecryptReadBackend;
use crate::blob::{NodeStreamer, NodeType};
use crate::blob::{NodeStreamer, NodeType, Tree};
use crate::index::IndexBackend;
use crate::repo::SnapshotFile;
#[derive(Parser)]
pub(super) struct Opts {
/// reference snapshot
id1: String,
/// reference snapshot/path
#[clap(value_name = "SNAPSHOT1[:PATH1]")]
snap1: String,
/// new snapshot
id2: String,
/// new snapshot/path [default for PATH2: PATH1]
#[clap(value_name = "SNAPSHOT2[:PATH2]")]
snap2: String,
}
pub(super) async fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts) -> Result<()> {
let (id1, path1) = opts.snap1.split_once(':').unwrap_or((&opts.snap1, ""));
let (id2, path2) = opts.snap2.split_once(':').unwrap_or((&opts.snap2, path1));
v1!("getting snapshots...");
let snaps = SnapshotFile::from_ids(be, &[opts.id1, opts.id2]).await?;
let snaps = SnapshotFile::from_ids(be, &[id1.to_string(), id2.to_string()]).await?;
let snap1 = &snaps[0];
let snap2 = &snaps[1];
let index = IndexBackend::new(be, progress_counter()).await?;
let id1 = Tree::subtree_id(&index, snap1.tree, Path::new(path1)).await?;
let id2 = Tree::subtree_id(&index, snap2.tree, Path::new(path2)).await?;
let mut tree_streamer1 = NodeStreamer::new(index.clone(), snap1.tree).await?;
let mut tree_streamer2 = NodeStreamer::new(index, snap2.tree).await?;
let mut tree_streamer1 = NodeStreamer::new(index.clone(), id1).await?;
let mut tree_streamer2 = NodeStreamer::new(index, id2).await?;
let mut item1 = tree_streamer1.next().await.transpose()?;
let mut item2 = tree_streamer2.next().await.transpose()?;

View File

@ -1,24 +1,28 @@
use anyhow::Result;
use clap::Parser;
use futures::StreamExt;
use std::path::Path;
use super::progress_counter;
use crate::backend::DecryptReadBackend;
use crate::blob::NodeStreamer;
use crate::blob::{NodeStreamer, Tree};
use crate::index::IndexBackend;
use crate::repo::SnapshotFile;
#[derive(Parser)]
pub(super) struct Opts {
/// snapshot to ls
id: String,
/// snapshot/path to ls
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
}
pub(super) async fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts) -> Result<()> {
let snap = SnapshotFile::from_str(be, &opts.id, |_| true, progress_counter()).await?;
let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, ""));
let snap = SnapshotFile::from_str(be, id, |_| true, progress_counter()).await?;
let index = IndexBackend::new(be, progress_counter()).await?;
let tree = Tree::subtree_id(&index, snap.tree, Path::new(path)).await?;
let mut tree_streamer = NodeStreamer::new(index, snap.tree).await?;
let mut tree_streamer = NodeStreamer::new(index, tree).await?;
while let Some(item) = tree_streamer.next().await {
let (path, _) = item?;
println!("{:?} ", path);

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::io::Read;
use std::num::NonZeroU32;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use anyhow::{anyhow, Result};
use clap::Parser;
@ -12,7 +12,7 @@ use vlog::*;
use super::{bytes, progress_bytes, progress_counter};
use crate::backend::{DecryptReadBackend, FileType, LocalBackend};
use crate::blob::{Node, NodeStreamer, NodeType};
use crate::blob::{Node, NodeStreamer, NodeType, Tree};
use crate::crypto::hash;
use crate::id::Id;
use crate::index::{IndexBackend, IndexedBackend};
@ -32,21 +32,25 @@ pub(super) struct Opts {
#[clap(long)]
numeric_id: bool,
/// snapshot to restore
id: String,
/// snapshot/path to restore
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
/// restore destination
dest: String,
}
pub(super) async fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts) -> Result<()> {
let snap = SnapshotFile::from_str(be, &opts.id, |_| true, progress_counter()).await?;
let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, ""));
let snap = SnapshotFile::from_str(be, id, |_| true, progress_counter()).await?;
let index = IndexBackend::new(be, progress_counter()).await?;
let tree = Tree::subtree_id(&index, snap.tree, Path::new(path)).await?;
let dest = LocalBackend::new(&opts.dest);
let index = IndexBackend::new(be, progress_counter()).await?;
v1!("collecting restore information and allocating non-existing files...");
let file_infos = allocate_and_collect(&dest, index.clone(), snap.tree, &opts).await?;
let file_infos = allocate_and_collect(&dest, index.clone(), tree, &opts).await?;
v1!("total restore size: {}", bytes(file_infos.total_size));
if file_infos.matched_size > 0 {
v1!(
@ -63,7 +67,7 @@ pub(super) async fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts)
}
v1!("setting metadata...");
restore_metadata(&dest, index, snap.tree, &opts).await?;
restore_metadata(&dest, index, tree, &opts).await?;
v1!("done.");
Ok(())