summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-08-02 07:12:18 +0000
committerLars Wirzenius <liw@liw.fi>2021-08-02 07:12:18 +0000
commit8c2f79fa3cecadd8e7d7f74df6938b25c81c5e9e (patch)
treeb25ed37fcfb2f8805a5b298b5ba09b80922a3c11
parent4c6206a2cb1f8c1efba9fd08a4d6b4fa7d09a6dc (diff)
parent1962cdaebc71bec28861635784e0b41deac98060 (diff)
downloadobnam2-8c2f79fa3cecadd8e7d7f74df6938b25c81c5e9e.tar.gz
Merge branch 'resolve' into 'main'
add "obnam resolve" Closes #118 and #135 See merge request obnam/obnam!171
-rw-r--r--obnam.md2
-rw-r--r--src/backup_progress.rs3
-rw-r--r--src/backup_run.rs8
-rw-r--r--src/bin/obnam.rs3
-rw-r--r--src/client.rs16
-rw-r--r--src/cmd/list_files.rs2
-rw-r--r--src/cmd/mod.rs1
-rw-r--r--src/cmd/resolve.rs33
-rw-r--r--src/cmd/restore.rs4
-rw-r--r--src/cmd/show_gen.rs2
-rw-r--r--src/generation.rs34
-rw-r--r--src/genlist.rs10
12 files changed, 92 insertions, 26 deletions
diff --git a/obnam.md b/obnam.md
index decb28c..5386dea 100644
--- a/obnam.md
+++ b/obnam.md
@@ -1278,6 +1278,8 @@ when I run obnam backup
then backup generation is GEN
when I run obnam list
then generation list contains <GEN>
+when I run obnam resolve latest
+then generation list contains <GEN>
when I invoke obnam restore <GEN> rest
given a manifest of the directory live restored in rest in rest.yaml
then manifests live.yaml and rest.yaml match
diff --git a/src/backup_progress.rs b/src/backup_progress.rs
index fd07978..30b6228 100644
--- a/src/backup_progress.rs
+++ b/src/backup_progress.rs
@@ -1,3 +1,4 @@
+use crate::generation::GenId;
use indicatif::{ProgressBar, ProgressStyle};
use std::path::Path;
@@ -58,7 +59,7 @@ impl BackupProgress {
Self { progress }
}
- pub fn download_generation(gen_id: &str) -> Self {
+ pub fn download_generation(gen_id: &GenId) -> Self {
let progress = ProgressBar::new(0);
let parts = vec!["{msg}", "elapsed: {elapsed}", "{spinner}"];
progress.set_style(ProgressStyle::default_bar().template(&parts.join("\n")));
diff --git a/src/backup_run.rs b/src/backup_run.rs
index dee1d11..3b107bb 100644
--- a/src/backup_run.rs
+++ b/src/backup_run.rs
@@ -6,7 +6,9 @@ use crate::config::ClientConfig;
use crate::error::ObnamError;
use crate::fsentry::FilesystemEntry;
use crate::fsiter::{AnnotatedFsEntry, FsIterError, FsIterator};
-use crate::generation::{LocalGeneration, LocalGenerationError, NascentError, NascentGeneration};
+use crate::generation::{
+ GenId, LocalGeneration, LocalGenerationError, NascentError, NascentGeneration,
+};
use crate::policy::BackupPolicy;
use log::{info, warn};
use std::path::{Path, PathBuf};
@@ -72,7 +74,7 @@ impl<'a> BackupRun<'a> {
pub fn start(
&mut self,
- genid: Option<&str>,
+ genid: Option<&GenId>,
oldname: &Path,
) -> Result<LocalGeneration, ObnamError> {
match genid {
@@ -94,7 +96,7 @@ impl<'a> BackupRun<'a> {
fn fetch_previous_generation(
&self,
- genid: &str,
+ genid: &GenId,
oldname: &Path,
) -> Result<LocalGeneration, ObnamError> {
let progress = BackupProgress::download_generation(genid);
diff --git a/src/bin/obnam.rs b/src/bin/obnam.rs
index 29de2eb..b2d5683 100644
--- a/src/bin/obnam.rs
+++ b/src/bin/obnam.rs
@@ -9,6 +9,7 @@ use obnam::cmd::get_chunk::GetChunk;
use obnam::cmd::init::Init;
use obnam::cmd::list::List;
use obnam::cmd::list_files::ListFiles;
+use obnam::cmd::resolve::Resolve;
use obnam::cmd::restore::Restore;
use obnam::cmd::show_config::ShowConfig;
use obnam::cmd::show_gen::ShowGeneration;
@@ -44,6 +45,7 @@ fn main_program() -> anyhow::Result<()> {
Command::List(x) => x.run(&config),
Command::ShowGeneration(x) => x.run(&config),
Command::ListFiles(x) => x.run(&config),
+ Command::Resolve(x) => x.run(&config),
Command::Restore(x) => x.run(&config),
Command::GetChunk(x) => x.run(&config),
Command::Config(x) => x.run(&config),
@@ -102,6 +104,7 @@ enum Command {
ListFiles(ListFiles),
Restore(Restore),
ShowGeneration(ShowGeneration),
+ Resolve(Resolve),
GetChunk(GetChunk),
Config(ShowConfig),
EncryptChunk(EncryptChunk),
diff --git a/src/client.rs b/src/client.rs
index ca0104f..c655bb2 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -6,7 +6,7 @@ use crate::chunkmeta::ChunkMeta;
use crate::cipher::{CipherEngine, CipherError};
use crate::config::{ClientConfig, ClientConfigError};
use crate::fsentry::{FilesystemEntry, FilesystemKind};
-use crate::generation::{FinishedGeneration, LocalGeneration, LocalGenerationError};
+use crate::generation::{FinishedGeneration, GenId, LocalGeneration, LocalGenerationError};
use crate::genlist::GenerationList;
use chrono::{DateTime, Local};
@@ -101,16 +101,15 @@ impl AsyncBackupClient {
self.chunk_client.fetch_chunk(chunk_id).await
}
- async fn fetch_generation_chunk(&self, gen_id: &str) -> Result<GenerationChunk, ClientError> {
- let chunk_id = ChunkId::recreate(gen_id);
- let chunk = self.fetch_chunk(&chunk_id).await?;
+ async fn fetch_generation_chunk(&self, gen_id: &GenId) -> Result<GenerationChunk, ClientError> {
+ let chunk = self.fetch_chunk(gen_id.as_chunk_id()).await?;
let gen = GenerationChunk::from_data_chunk(&chunk)?;
Ok(gen)
}
pub async fn fetch_generation(
&self,
- gen_id: &str,
+ gen_id: &GenId,
dbname: &Path,
) -> Result<LocalGeneration, ClientError> {
let gen = self.fetch_generation_chunk(gen_id).await?;
@@ -323,16 +322,15 @@ impl BackupClient {
self.chunk_client.fetch_chunk(chunk_id)
}
- fn fetch_generation_chunk(&self, gen_id: &str) -> Result<GenerationChunk, ClientError> {
- let chunk_id = ChunkId::recreate(gen_id);
- let chunk = self.fetch_chunk(&chunk_id)?;
+ fn fetch_generation_chunk(&self, gen_id: &GenId) -> Result<GenerationChunk, ClientError> {
+ let chunk = self.fetch_chunk(gen_id.as_chunk_id())?;
let gen = GenerationChunk::from_data_chunk(&chunk)?;
Ok(gen)
}
pub fn fetch_generation(
&self,
- gen_id: &str,
+ gen_id: &GenId,
dbname: &Path,
) -> Result<LocalGeneration, ClientError> {
let gen = self.fetch_generation_chunk(gen_id)?;
diff --git a/src/cmd/list_files.rs b/src/cmd/list_files.rs
index e511327..bdec55b 100644
--- a/src/cmd/list_files.rs
+++ b/src/cmd/list_files.rs
@@ -25,7 +25,7 @@ impl ListFiles {
let client = AsyncBackupClient::new(config)?;
let genlist = client.list_generations().await?;
- let gen_id: String = genlist.resolve(&self.gen_id)?;
+ let gen_id = genlist.resolve(&self.gen_id)?;
let gen = client.fetch_generation(&gen_id, temp.path()).await?;
for file in gen.files()?.iter()? {
diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs
index 7313a05..502ec5d 100644
--- a/src/cmd/mod.rs
+++ b/src/cmd/mod.rs
@@ -5,6 +5,7 @@ pub mod get_chunk;
pub mod init;
pub mod list;
pub mod list_files;
+pub mod resolve;
pub mod restore;
pub mod show_config;
pub mod show_gen;
diff --git a/src/cmd/resolve.rs b/src/cmd/resolve.rs
new file mode 100644
index 0000000..9b36445
--- /dev/null
+++ b/src/cmd/resolve.rs
@@ -0,0 +1,33 @@
+use crate::client::AsyncBackupClient;
+use crate::config::ClientConfig;
+use crate::error::ObnamError;
+use structopt::StructOpt;
+use tokio::runtime::Runtime;
+
+#[derive(Debug, StructOpt)]
+pub struct Resolve {
+ generation: String,
+}
+
+impl Resolve {
+ pub fn run(&self, config: &ClientConfig) -> Result<(), ObnamError> {
+ let rt = Runtime::new()?;
+ rt.block_on(self.run_async(config))
+ }
+
+ async fn run_async(&self, config: &ClientConfig) -> Result<(), ObnamError> {
+ let client = AsyncBackupClient::new(config)?;
+ let generations = client.list_generations().await?;
+
+ match generations.resolve(&self.generation) {
+ Err(err) => {
+ return Err(err.into());
+ }
+ Ok(gen_id) => {
+ println!("{}", gen_id.as_chunk_id());
+ }
+ };
+
+ Ok(())
+ }
+}
diff --git a/src/cmd/restore.rs b/src/cmd/restore.rs
index c770501..e03f3de 100644
--- a/src/cmd/restore.rs
+++ b/src/cmd/restore.rs
@@ -40,8 +40,8 @@ impl Restore {
let client = AsyncBackupClient::new(config)?;
let genlist = client.list_generations().await?;
- let gen_id: String = genlist.resolve(&self.gen_id)?;
- info!("generation id is {}", gen_id);
+ let gen_id = genlist.resolve(&self.gen_id)?;
+ info!("generation id is {}", gen_id.as_chunk_id());
let gen = client.fetch_generation(&gen_id, temp.path()).await?;
info!("restoring {} files", gen.file_count()?);
diff --git a/src/cmd/show_gen.rs b/src/cmd/show_gen.rs
index 8df26c2..fb7e1bd 100644
--- a/src/cmd/show_gen.rs
+++ b/src/cmd/show_gen.rs
@@ -24,7 +24,7 @@ impl ShowGeneration {
let client = AsyncBackupClient::new(config)?;
let genlist = client.list_generations().await?;
- let gen_id: String = genlist.resolve(&self.gen_id)?;
+ let gen_id = genlist.resolve(&self.gen_id)?;
let gen = client.fetch_generation(&gen_id, temp.path()).await?;
let mut files = gen.files()?;
let mut files = files.iter()?;
diff --git a/src/generation.rs b/src/generation.rs
index 25fc14d..5412ae7 100644
--- a/src/generation.rs
+++ b/src/generation.rs
@@ -4,11 +4,37 @@ use crate::chunkid::ChunkId;
use crate::fsentry::FilesystemEntry;
use log::debug;
use rusqlite::Connection;
+use std::fmt;
use std::path::{Path, PathBuf};
/// An identifier for a file in a generation.
type FileId = i64;
+/// An identifier for a generation.
+#[derive(Debug, Clone)]
+pub struct GenId {
+ id: ChunkId,
+}
+
+impl GenId {
+ pub fn from_chunk_id(id: ChunkId) -> Self {
+ Self { id }
+ }
+
+ pub fn as_chunk_id(&self) -> &ChunkId {
+ &self.id
+ }
+}
+
+impl fmt::Display for GenId {
+ /// Format an identifier for display.
+ ///
+ /// The output can be parsed to re-created an identical identifier.
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.id)
+ }
+}
+
/// A nascent backup generation.
///
/// A nascent generation is one that is being prepared. It isn't
@@ -97,21 +123,21 @@ impl NascentGeneration {
/// A generation is finished when it's on the server. It can be restored.
#[derive(Debug, Clone)]
pub struct FinishedGeneration {
- id: ChunkId,
+ id: GenId,
ended: String,
}
impl FinishedGeneration {
pub fn new(id: &str, ended: &str) -> Self {
- let id = id.parse().unwrap(); // this never fails
+ let id = GenId::from_chunk_id(id.parse().unwrap()); // this never fails
Self {
id,
ended: ended.to_string(),
}
}
- pub fn id(&self) -> ChunkId {
- self.id.clone()
+ pub fn id(&self) -> &GenId {
+ &self.id
}
pub fn ended(&self) -> &str {
diff --git a/src/genlist.rs b/src/genlist.rs
index 21f558c..a81a997 100644
--- a/src/genlist.rs
+++ b/src/genlist.rs
@@ -1,5 +1,5 @@
use crate::chunkid::ChunkId;
-use crate::generation::FinishedGeneration;
+use crate::generation::{FinishedGeneration, GenId};
pub struct GenerationList {
list: Vec<FinishedGeneration>,
@@ -22,17 +22,17 @@ impl GenerationList {
self.list.iter()
}
- pub fn resolve(&self, genref: &str) -> Result<String, GenerationListError> {
+ pub fn resolve(&self, genref: &str) -> Result<GenId, GenerationListError> {
let gen = if self.list.is_empty() {
None
} else if genref == "latest" {
let i = self.list.len() - 1;
Some(self.list[i].clone())
} else {
- let genref: ChunkId = genref.parse().unwrap();
+ let genref = GenId::from_chunk_id(genref.parse().unwrap());
let hits: Vec<FinishedGeneration> = self
.iter()
- .filter(|gen| gen.id() == genref)
+ .filter(|gen| gen.id().as_chunk_id() == genref.as_chunk_id())
.cloned()
.collect();
if hits.len() == 1 {
@@ -45,7 +45,7 @@ impl GenerationList {
None => Err(GenerationListError::UnknownGeneration(ChunkId::recreate(
genref,
))),
- Some(gen) => Ok(gen.id().to_string()),
+ Some(gen) => Ok(gen.id().clone()),
}
}
}