Skip to content

Commit

Permalink
Merge branch 'main'
Browse files Browse the repository at this point in the history
  • Loading branch information
aawsome committed Sep 18, 2024
2 parents 47b1950 + ee6380e commit d5f7cb4
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 95 deletions.
24 changes: 7 additions & 17 deletions config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,23 +228,13 @@ to global and backup hooks. only apply
| keep-none | Allow to keep no snapshots. | false | true |
| prune | If set to true, prune the repository after snapshots have been removed. | false | |

### Copy Targets `[[copy.targets]]`

**Note**: Copy-targets are simply repositories with the same defaults as within
the repository section.

| Attribute | Description | Default Value | Example Value |
| ---------------- | ----------------------------------------------------------------- | ------------------------ | ---------------------- |
| cache-dir | Path to the cache directory for the target repository. | ~/.cache/rustic/$REPO_ID | ~/.cache/my_own_cache/ |
| no-cache | If true, disables caching for the target repository. | false | |
| password | The password for the target repository. | Not set | |
| password-file | Path to a file containing the password for the target repository. | Not set | |
| password-command | Command to retrieve the password for the target repository. | Not set | |
| repository | The path or URL to the target repository. | Not set | |
| repo-hot | The path or URL to the hot target repository. | Not set | |
| warm-up | If true, warms up the target repository by file access. | Not set | |
| warm-up-command | Command to warm up the target repository. | Not set | |
| warm-up-wait | The wait time for warming up the target repository. | Not set | |
### Copy Targets `[copy]`

**Note**: Copy-targets must be defined in their own config profile files.

| Attribute | Description | Default Value | Example Value |
| --------- | ------------------ | ------------- | ------------------------ |
| targets | Targets to copy to | [] | ["profile1", "profile2"] |

### WebDAV Options `[webdav]`

Expand Down
14 changes: 3 additions & 11 deletions config/copy_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@
repository = "/tmp/repo"
password = "test"

# you can specify multiple targets
[[copy.targets]]
repository = "/tmp/repo2"
password = "test"
no-cache = true

[[copy.targets]]
repository = "rclone:ovh:backup"
repo-hot = "clone:ovh:backup-hot"
password-file = "/root/key-rustic-ovh"
cache-dir = "/var/lib/cache/rustic" # explicitly specify cache dir for remote repository
# you can specify multiple targets. Note that each target must be configured via a config profile file
[copy]
targets = ["full", "rustic"]
21 changes: 2 additions & 19 deletions config/full-one.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,25 +145,8 @@ keep-withing-quarter-yearly = "0 year"
keep-withing-half-yearly = "1 year"
keep-within-yearly = "10 years"

# Multiple targets are available for the copy command. Each specify a repository with exactly identical options as in
# the [repository] section.
[[copy.targets]]
repository = "/repo/rustic" # Must be set
repo-hot = "/my/hot/repo" # Default: not set
# one of the three password options must be set
password = "mySecretPassword"
password-file = "/my/password.txt"
password-command = "my_command.sh"
no-cache = false
cache-dir = "/my/rustic/cachedir" # Default: Applications default cache dir, e.g. ~/.cache/rustic
# use either warm-up (warm-up by file access) or warm-up-command to specify warming up
warm-up = false
warm-up-command = "warmup.sh %id" # Default: not set
warm-up-wait = "10min" # Default: not set

[[copy.targets]]
repository = "/repo/rustic2" # Must be set
# ...
[copy]
targets = ["target_profile"] # Default: []

[webdav]
address = "localhost:8000"
Expand Down
21 changes: 2 additions & 19 deletions config/full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -176,25 +176,8 @@ keep-withing-quarter-yearly = "0 year"
keep-withing-half-yearly = "1 year"
keep-within-yearly = "10 years"

# Multiple targets are available for the copy command. Each specify a repository with exactly identical options as in
# the [repository] section.
[[copy.targets]]
repository = "/repo/rustic" # Must be set
repo-hot = "/my/hot/repo" # Default: not set
# one of the three password options must be set
password = "mySecretPassword"
password-file = "/my/password.txt"
password-command = "my_command.sh"
no-cache = false
cache-dir = "/my/rustic/cachedir" # Default: Applications default cache dir, e.g. ~/.cache/rustic
# use either warm-up (warm-up by file access) or warm-up-command to specify warming up
warm-up = false
warm-up-command = "warmup.sh %id" # Default: not set
warm-up-wait = "10min" # Default: not set

[[copy.targets]]
repository = "/repo/rustic2" # Must be set
# ...
[copy]
targets = ["profile1", "profile2"] # Default: []

[webdav]
address = "localhost:8000"
Expand Down
1 change: 1 addition & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ impl Configurable<RusticConfig> for EntryPoint {

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

Expand Down
21 changes: 18 additions & 3 deletions src/commands/backup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
use std::path::PathBuf;

use crate::{
commands::{get_repository, init::init, open_repository},
commands::{get_repository, init::init, open_repository, snapshots::fill_table},
config::Hooks,
helpers::bytes_size_to_string,
helpers::{bold_cell, bytes_size_to_string, table},
status_err, Application, RUSTIC_APP,
};

use abscissa_core::{Command, Runnable, Shutdown};
use anyhow::{bail, Context, Result};
use clap::ValueHint;
use comfy_table::Cell;
use log::{debug, error, info, warn};
use merge::Merge;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -64,9 +65,14 @@ pub struct BackupCmd {
#[merge(strategy = merge::bool::overwrite_false)]
json: bool,

/// Don't show any output
/// Show detailed information about generated snapshot
#[clap(long, conflicts_with = "json")]
#[merge(strategy = merge::bool::overwrite_false)]
long: bool,

/// Don't show any output
#[clap(long, conflicts_with_all = ["json", "long"])]
#[merge(strategy = merge::bool::overwrite_false)]
quiet: bool,

/// Initialize repository, if it doesn't exist yet
Expand Down Expand Up @@ -298,6 +304,15 @@ fn backup_source<P: ProgressBars, S: IndexedIds>(
if opts.json {
let mut stdout = std::io::stdout();
serde_json::to_writer_pretty(&mut stdout, &snap)?;
} else if opts.long {
let mut table = table();

let add_entry = |title: &str, value: String| {
_ = table.add_row([bold_cell(title), Cell::new(value)]);
};
fill_table(&snap, add_entry);

println!("{table}");
} else if !opts.quiet {
let summary = snap.summary.unwrap();
println!(
Expand Down
53 changes: 38 additions & 15 deletions src/commands/copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,55 @@

use crate::{
commands::{get_repository, init::init_password, open_repository, open_repository_indexed},
config::AllRepositoryOptions,
helpers::table_with_titles,
status_err, Application, RUSTIC_APP,
status_err, Application, RusticConfig, RUSTIC_APP,
};
use abscissa_core::{Command, Runnable, Shutdown};
use abscissa_core::{config::Override, Command, FrameworkError, Runnable, Shutdown};
use anyhow::{bail, Result};
use log::{error, info};
use log::{error, info, log, Level};
use merge::Merge;
use serde::{Deserialize, Serialize};

use rustic_core::{CopySnapshot, Id, KeyOptions};

/// `copy` subcommand
#[derive(clap::Parser, Command, Debug)]
pub(crate) struct CopyCmd {
#[derive(clap::Parser, Command, Default, Clone, Debug, Serialize, Deserialize, Merge)]
pub struct CopyCmd {
/// Snapshots to copy. If none is given, use filter options to filter from all snapshots.
#[clap(value_name = "ID")]
#[serde(skip)]
#[merge(skip)]
ids: Vec<String>,

/// Initialize non-existing target repositories
#[clap(long)]
#[serde(skip)]
#[merge(skip)]
init: bool,

/// Target repository (can be specified multiple times)
#[clap(long = "target", value_name = "TARGET")]
#[merge(strategy = merge::vec::overwrite_empty)]
targets: Vec<String>,

/// Key options (when using --init)
#[clap(flatten, next_help_heading = "Key options (when using --init)")]
#[serde(skip)]
#[merge(skip)]
key_opts: KeyOptions,
}

/// Target repository options
#[derive(Default, Clone, Debug, Serialize, Deserialize, Merge)]
pub struct Targets {
/// Target repositories
#[merge(strategy = merge::vec::overwrite_empty)]
targets: Vec<AllRepositoryOptions>,
impl Override<RusticConfig> for CopyCmd {
// 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 "copy" section from config file, if given
self_config.merge(config.copy);
config.copy = self_config;
Ok(config)
}
}

impl Runnable for CopyCmd {
Expand All @@ -52,8 +67,7 @@ impl CopyCmd {
let config = RUSTIC_APP.config();

if config.copy.targets.is_empty() {
status_err!("no [[copy.targets]] section in config file found!");
RUSTIC_APP.shutdown(Shutdown::Crash);
bail!("No target given. Please specify at least 1 target either in the profile or using --target!");
}

let repo = open_repository_indexed(&config.repository)?;
Expand All @@ -66,7 +80,16 @@ impl CopyCmd {
snapshots.sort_unstable();

let poly = repo.config().poly()?;
for target_opt in &config.copy.targets {
for target in &config.copy.targets {
let mut merge_logs = Vec::new();
let mut target_config = RusticConfig::default();
target_config.merge_profile(target, &mut merge_logs, Level::Error)?;
// display logs from merging
for (level, merge_log) in merge_logs {
log!(level, "{}", merge_log);
}
let target_opt = &target_config.repository;

let repo_dest = get_repository(target_opt)?;

info!("copying to target {}...", repo_dest.name);
Expand Down
5 changes: 4 additions & 1 deletion src/commands/forget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ impl Runnable for ForgetCmd {
}

impl ForgetCmd {
/// be careful about self vs `RUSTIC_APP.config()` usage
/// only the `RUSTIC_APP.config()` involves the TOML and ENV merged configurations
/// see <https://github.com/rustic-rs/rustic/issues/1242>
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = open_repository(&config.repository)?;
Expand Down Expand Up @@ -155,7 +158,7 @@ impl ForgetCmd {
(_, _, true) => {}
}

if self.config.prune {
if config.forget.prune {
let mut prune_opts = self.prune_opts.clone();
prune_opts.opts.ignore_snaps = forget_snaps;
prune_opts.run();
Expand Down
6 changes: 4 additions & 2 deletions src/commands/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,10 @@ pub fn snap_to_table(sn: &SnapshotFile, count: usize) -> [String; 9] {
pub fn fill_table(snap: &SnapshotFile, mut add_entry: impl FnMut(&str, String)) {
add_entry("Snapshot", snap.id.to_hex().to_string());
// note that if original was not set, it is set to snap.id by the load process
if snap.original != Some(snap.id) {
add_entry("Original ID", snap.original.unwrap().to_hex().to_string());
if let Some(original) = snap.original {
if original != snap.id {
add_entry("Original ID", original.to_hex().to_string());
}
}
add_entry("Time", snap.time.format("%Y-%m-%d %H:%M:%S").to_string());
add_entry("Generated by", snap.program_version.clone());
Expand Down
18 changes: 12 additions & 6 deletions src/commands/webdav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,43 +66,49 @@ impl Runnable for WebDavCmd {
}

impl WebDavCmd {
/// be careful about self VS RUSTIC_APP.config() usage
/// only the RUSTIC_APP.config() involves the TOML and ENV merged configurations
/// see https://github.com/rustic-rs/rustic/issues/1242
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = open_repository_indexed(&config.repository)?;

let path_template = self
let path_template = config
.webdav
.path_template
.clone()
.unwrap_or_else(|| "[{hostname}]/[{label}]/{time}".to_string());
let time_template = self
let time_template = config
.webdav
.time_template
.clone()
.unwrap_or_else(|| "%Y-%m-%d_%H-%M-%S".to_string());

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

let vfs = if let Some(snap) = &self.snapshot_path {
let vfs = if let Some(snap) = &config.webdav.snapshot_path {
let node = repo.node_from_snapshot_path(snap, sn_filter)?;
Vfs::from_dir_node(&node)
} else {
let snapshots = repo.get_matching_snapshots(sn_filter)?;
let (latest, identical) = if self.symlinks {
let (latest, identical) = if config.webdav.symlinks {
(Latest::AsLink, IdenticalSnapshot::AsLink)
} else {
(Latest::AsDir, IdenticalSnapshot::AsDir)
};
Vfs::from_snapshots(snapshots, &path_template, &time_template, latest, identical)?
};

let addr = self
let addr = config
.webdav
.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(
let file_access = config.webdav.file_access.as_ref().map_or_else(
|| {
if repo.config().is_hot == Some(true) {
Ok(FilePolicy::Forbidden)
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use serde_with::{serde_as, OneOrMany};
#[cfg(feature = "webdav")]
use crate::commands::webdav::WebDavCmd;
use crate::{
commands::{backup::BackupCmd, copy::Targets, forget::ForgetOptions},
commands::{backup::BackupCmd, copy::CopyCmd, forget::ForgetOptions},
config::progress_options::ProgressOptions,
filtering::SnapshotFilter,
};
Expand Down Expand Up @@ -57,7 +57,7 @@ pub struct RusticConfig {

/// Copy options
#[clap(skip)]
pub copy: Targets,
pub copy: CopyCmd,

/// Forget options
#[clap(skip)]
Expand Down
1 change: 1 addition & 0 deletions tests/show-config-fixtures/empty.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ with-atime = false
ignore-devid = false
no-scan = false
json = false
long = false
quiet = false
init = false
skip-identical-parent = false
Expand Down

0 comments on commit d5f7cb4

Please sign in to comment.