Skip to content

Commit

Permalink
Add config support for webdav
Browse files Browse the repository at this point in the history
  • Loading branch information
aawsome committed Jan 29, 2024
1 parent a9629bd commit 546fe6f
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 22 deletions.
8 changes: 8 additions & 0 deletions config/full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,11 @@ warm-up-wait = "10min" # Default: not set
[[copy.targets]]
repository = "/repo/rustic2" # Must be set
# ...

[webdav]
address = "localhost:8000"
path-template = "[{hostname}]/[{label}]/{time}" # only relevant if no snapshot-path is given
time-template = "%Y-%m-%d_%H-%M-%S" # only relevant if no snapshot-path is given
symlinks = false
file-access = "read" # Default: "forbidden" for hot/cold repos, else "read"
snapshot-path = "latest:/dir" # Default: not set - this will generate a virtual tree with all snapshots using path-template
2 changes: 2 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ impl Configurable<RusticConfig> for EntryPoint {

match &self.commands {
RusticCmd::Forget(cmd) => cmd.override_config(config),
#[cfg(feature = "webdav")]
RusticCmd::Webdav(cmd) => cmd.override_config(config),

// subcommands that don't need special overrides use a catch all
_ => Ok(config),
Expand Down
69 changes: 47 additions & 22 deletions src/commands/webdav.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
//! `webdav` subcommand
use std::net::ToSocketAddrs;
use std::{net::ToSocketAddrs, str::FromStr};

use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
use abscissa_core::{Command, Runnable, Shutdown};
use crate::{commands::open_repository, status_err, Application, RusticConfig, RUSTIC_APP};
use abscissa_core::{config::Override, Command, FrameworkError, Runnable, Shutdown};
use anyhow::{anyhow, Result};
use dav_server::{warp::dav_handler, DavHandler};
use merge::Merge;
use serde::Deserialize;

use rustic_core::vfs::{FilePolicy, IdenticalSnapshot, Latest, Vfs};

#[derive(clap::Parser, Command, Debug)]
pub(crate) struct WebDavCmd {
/// Address to bind the webdav server to
#[clap(long, value_name = "ADDRESS", default_value = "localhost:8000")]
addr: String,
#[derive(Clone, Command, Default, Debug, clap::Parser, Deserialize, Merge)]
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
pub struct WebDavCmd {
/// Address to bind the webdav server to. [default: "localhost:8000"]
#[clap(long, value_name = "ADDRESS")]
address: Option<String>,

/// The path template to use for snapshots. {id}, {id_long}, {time}, {username}, {hostname}, {label}, {tags}, {backup_start}, {backup_end} are replaced. [default: "[{hostname}]/[{label}]/{time}"]
#[clap(long)]
Expand All @@ -23,15 +27,29 @@ pub(crate) struct WebDavCmd {

/// Use symlinks. This may not be supported by all WebDAV clients
#[clap(long)]
#[merge(strategy = merge::bool::overwrite_false)]
symlinks: bool,

/// How to handle access to files. Default: "forbidden" for hot/cold repositories, else "read"
/// How to handle access to files. [default: "forbidden" for hot/cold repositories, else "read"]
#[clap(long)]
file_access: Option<FilePolicy>,
file_access: Option<String>,

/// Specify directly which path to mount
/// Specify directly which snapshot/path to serve
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: Option<String>,
snapshot_path: Option<String>,
}

impl Override<RusticConfig> for WebDavCmd {
// Process the given command line options, overriding settings from
// a configuration file using explicit flags taken from command-line
// arguments.
fn override_config(&self, mut config: RusticConfig) -> Result<RusticConfig, FrameworkError> {
let mut self_config = self.clone();
// merge "webdav" section from config file, if given
self_config.merge(config.webdav);
config.webdav = self_config;
Ok(config)
}
}

impl Runnable for WebDavCmd {
Expand All @@ -48,14 +66,6 @@ impl WebDavCmd {
let config = RUSTIC_APP.config();
let repo = open_repository(&config.repository)?.to_indexed()?;

let file_access = self.file_access.unwrap_or_else(|| {
if repo.config().is_hot == Some(true) {
FilePolicy::Forbidden
} else {
FilePolicy::Read
}
});

let path_template = self
.path_template
.clone()
Expand All @@ -67,7 +77,7 @@ impl WebDavCmd {

let sn_filter = |sn: &_| config.snapshot_filter.matches(sn);

let vfs = if let Some(snap) = &self.snap {
let vfs = if let Some(snap) = &self.snapshot_path {
let node = repo.node_from_snapshot_path(snap, sn_filter)?;
Vfs::from_dirnode(node)
} else {
Expand All @@ -79,11 +89,26 @@ impl WebDavCmd {
};
Vfs::from_snapshots(snapshots, &path_template, &time_template, latest, identical)?
};

let addr = self
.addr
.address
.clone()
.unwrap_or_else(|| "localhost:8000".to_string())
.to_socket_addrs()?
.next()
.ok_or_else(|| anyhow!("no address given"))?;

let file_access = self.file_access.as_ref().map_or_else(
|| {
if repo.config().is_hot == Some(true) {
Ok(FilePolicy::Forbidden)
} else {
Ok(FilePolicy::Read)
}
},
|s| FilePolicy::from_str(s),
)?;

let dav_server = DavHandler::builder()
.filesystem(vfs.into_webdav_fs(repo, file_access))
.build_handler();
Expand Down
7 changes: 7 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use rustic_backend::BackendOptions;
use rustic_core::RepositoryOptions;
use serde::{Deserialize, Serialize};

#[cfg(feature = "webdav")]
use crate::commands::webdav::WebDavCmd;
use crate::{
commands::{backup::BackupCmd, copy::Targets, forget::ForgetOptions},
config::progress_options::ProgressOptions,
Expand Down Expand Up @@ -60,6 +62,11 @@ pub struct RusticConfig {
/// Forget options
#[clap(skip)]
pub forget: ForgetOptions,

#[cfg(feature = "webdav")]
/// webdav options
#[clap(skip)]
pub webdav: WebDavCmd,
}

#[derive(Clone, Default, Debug, Parser, Deserialize, Merge)]
Expand Down

0 comments on commit 546fe6f

Please sign in to comment.