From e642c307dc1ff13823ab26f20790e8f27e0556d4 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 17 Sep 2024 16:33:30 +0200 Subject: [PATCH] fix config files --- config/full-one.toml | 174 ----------------------- config/full.toml | 8 +- config/local.toml | 12 +- config/rustic.toml | 8 +- config/services/rclone_ovh-hot-cold.toml | 12 +- src/commands/backup.rs | 50 +++---- tests/show-config-fixtures/empty.txt | 4 +- 7 files changed, 48 insertions(+), 220 deletions(-) delete mode 100644 config/full-one.toml diff --git a/config/full-one.toml b/config/full-one.toml deleted file mode 100644 index ef8c456b6..000000000 --- a/config/full-one.toml +++ /dev/null @@ -1,174 +0,0 @@ -# Full rustic config file containing all options which are available through the config file. -# -# It uses short notations for options which allow multiple entries in an array but are also possible as single line -# For notations using arrays, see full.toml -# -# This file should be placed in the user's local config dir (~/.config/rustic/) or in the global -# config dir (/etc/rustic). -# If you save it under NAME.toml, use "rustic -P NAME" to access this profile. -# -# Note that most options can be overwritten by the corresponding command line option. - -# Global options: These options are used for all commands. -[global] -use-profiles = [] -log-level = "info" # any of "off", "error", "warn", "info", "debug", "trace"; default: "info" -log-file = "/path/to/rustic.log" # Default: not set -no-progress = false -progress-interval = "100ms" -dry-run = false -check-index = false - -# Global env variables: These are set by rustic before calling a subcommand, e.g. rclone or commands -# defined in the repository options. -[global.env] -# This is only an example how to set an rclone env variable. Default: No variables are defined. -RCLONE_XXX = "true" - -# Repository options: These options define which backend to use and which password to use. -[repository] -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" # Default: not set -warm-up-wait = "10min" # Default: not set - -# Additional repository options - depending on backend. These can be only set in the config file or using env variables. -# For env variables use upper snake case and prefix with "RUSTIC_REPO_OPT_", e.g. `use-passwort = "true"` becomes -# `RUSTIC_REPO_OPT_USE_PASSWORT=true` -[repository.options] -post-create-command = "par2create -qq -n1 -r5 %file" # Only local backend; Default: not set -post-delete-command = "sh -c \"rm -f %file*.par2\"" # Only local backend; Default: not set -retry = "default" # Only rest/rclone/all opendal backends; Allowed values: "false"/"off", "default" or number of retries -timeout = "10min" # Only rest/rclone backend -rclone-command = "rclone serve restic --addr localhost:0" # Only rclone; Default: not set -use-password = "true" # Only rclone -rest-url = "http://localhost:8000" # Only rclone; Default: determine REST URL from rclone output -connections = "20" # Only opendal backends; Default: Not set -throttle = "10kB,10MB" # limit and burst per second; only opendal backends; Default: Not set -# Note that opendal backends use several service-dependent options which may be specified here, see -# https://opendal.apache.org/docs/rust/opendal/services/index.html - -# Additional repository options for the hot part - depending on backend. These can be only set in the config file or -# using env variables. -# For env variables use upper snake case and prefix with "RUSTIC_REPO_OPTHOT_" -[repository.options-hot] -# see [repository.options] - -# Additional repository options for the cold part - depending on backend. These can be only set in the config file or -# using env variables. -# For env variables use upper snake case and prefix with "RUSTIC_REPO_OPTCOLD_" -[repository.options-cold] -# see [repository.options] - -# Snapshot-filter options: These options apply to all commands that use snapshot filters -[snapshot-filter] -filter-host = ["host2"] # Default: [] -filter-label = ["label1"] # Default: [] -filter-tags = ["tag1,tag2"] # Default: [] -filter-paths = ["path1"] # Default: [] -filter-fn = '|sn| {sn.host == "host1" || sn.description.contains("test")}' # Default: no filter function - -# Backup options: These options are used for all sources when calling the backup command. -# They can be overwritten by source-specific options (see below) or command line options. -[backup] -label = "label" # Default: not set -tag = "tag1" # Default: not set -description = "my description" # Default: not set -description-from = "/path/to/description.txt" # Default: not set -delete-never = false -delete-after = "5d" # Default: not set -host = "manually_set_host" # Default: host name -group-by = "host,label,paths" # Can be any combination of host,label,paths,tags -parent = "123abc" # Default: not set -force = false -ignore-ctime = false -ignore-inode = false -stdin-filename = "stdin" # Only for stdin source -as-path = "/my/path" # Default: not set; Note: This only works if source contains of a single path. -with-atime = false -ignore-devid = false -glob = "*.txt" # Default: not set -iglob = "*.txt" # Default: not set -glob-file = "glob-file.txt" # Default: not set -iglob-file = "glob-file.txt" # Default: not set -git-ignore = false -no-require-git = false -exclude-if-present = ".nobackup" # Default: not set -custom-ignorefile = ".rusticignore" # Default: not set -one-file-system = false -exclude-larger-than = "100MB" # Default: not set -json = false -init = false -no-scan = false -quiet = false -skip-identical-parent = false - -# Backup options for specific sources - all above options are also available here and replace them for the given source -[[backup.sources]] -source = "/path/to/source1" -label = "label" # Default: not set -# .. and so on. see [backup] - -# forget options -[forget] -prune = false -group-by = "host,label,paths" # Can be any combination of host,label,paths,tags -# The following filter options can be also defined here and then overwrite the options for the forget command -filter-host = ["host2"] # Default: [] -filter-label = ["label1"] # Default: [] -filter-tags = ["tag1,tag2"] # Default: [] -filter-paths = ["path1"] # Default: no paths filter -filter-fn = '|sn| {sn.host == "host1" || sn.description.contains("test")}' # Default: no filter function -# The retention options follow. All of these are not set by default. -keep-tags = "tag1" # Default: not set -keep-ids = "123abc" # Keep all snapshots whose ID starts with any of these strings, default: not set -keep-last = 0 -keep-daily = 3 -keep-weekly = 0 -keep-monthly = 0 -keep-quarter-yearly = 0 -keep-half-yearly = 0 -keep-yearly = 10 -keep-within = "0s" -keep-within-daily = "0 seconds" -keep-within-weekly = "2 months" -keep-withing-monthly = "1 year" -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 -# ... - -[webdav] -address = "localhost:8000" -path-template = "[{hostname}]/[{label}]/{time}" # 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}"]. 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 - if not set, generate a virtual tree with all snapshots using path-template diff --git a/config/full.toml b/config/full.toml index 08a702c71..42a2615d1 100644 --- a/config/full.toml +++ b/config/full.toml @@ -109,13 +109,13 @@ quiet = false skip-identical-parent = false # Backup options for specific sources - all above options are also available here and replace them for the given source -[[backup.sources]] -source = "/path/to/source1" +[[backup.snapshots]] +sources = ["/path/to/source1"] label = "label" # Default: not set # .. and so on. see [backup] -[[backup.sources]] -source = [ +[[backup.snapshots]] +sources = [ "/path/to/source2", "/second/path", ] # multiple local paths are given as array diff --git a/config/local.toml b/config/local.toml index 9b378e821..23f63decd 100644 --- a/config/local.toml +++ b/config/local.toml @@ -20,12 +20,12 @@ exclude-if-present = [".nobackup", "CACHEDIR.TAG"] glob-file = ["/root/rustic-local.glob"] one-file-system = true -[[backup.sources]] -source = "/home" +[[backup.snapshots]] +sources = ["/home"] git-ignore = true -[[backup.sources]] -source = "/etc" +[[backup.snapshots]] +sources = ["/etc"] -[[backup.sources]] -source = "/root" +[[backup.snapshots]] +sources = ["/root"] diff --git a/config/rustic.toml b/config/rustic.toml index 850821984..4e870ca53 100644 --- a/config/rustic.toml +++ b/config/rustic.toml @@ -29,11 +29,11 @@ git-ignore = true # # Note that if you call "rustic backup" without any source, all sources from this config # file will be processed. -[[backup.sources]] -source = "/data/dir" +[[backup.snapshots]] +sources = ["/data/dir"] -[[backup.sources]] -source = "/home" +[[backup.snapshots]] +sources = ["/home"] glob = ["!/home/*/Downloads/*"] # forget options diff --git a/config/services/rclone_ovh-hot-cold.toml b/config/services/rclone_ovh-hot-cold.toml index ee490b0d1..e5ea95621 100644 --- a/config/services/rclone_ovh-hot-cold.toml +++ b/config/services/rclone_ovh-hot-cold.toml @@ -23,12 +23,12 @@ exclude-if-present = [".nobackup", "CACHEDIR.TAG"] glob-file = ["/root/rustic-ovh.glob"] one-file-system = true -[[backup.sources]] -source = "/home" +[[backup.snapshots]] +sources = ["/home"] git-ignore = true -[[backup.sources]] -source = "/etc" +[[backup.snapshots]] +sources = ["/etc"] -[[backup.sources]] -source = "/root" +[[backup.snapshots]] +sources = ["/root"] diff --git a/src/commands/backup.rs b/src/commands/backup.rs index 464b04033..cc8c42f7d 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -14,7 +14,7 @@ use clap::ValueHint; use log::{debug, info, warn}; use merge::Merge; use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, OneOrMany}; +use serde_with::serde_as; use rustic_core::{ BackupOptions, ConfigOptions, KeyOptions, LocalSourceFilterOptions, LocalSourceSaveOptions, @@ -100,16 +100,15 @@ pub struct BackupCmd { #[merge(skip)] config_opts: ConfigOptions, - /// Backup sources + /// Backup snapshots to generate #[clap(skip)] #[merge(strategy = merge_sources)] - sources: Vec, + snapshots: Vec, /// Backup source, used within config file #[clap(skip)] #[merge(skip)] - #[serde_as(as = "OneOrMany<_>")] - source: Vec, + sources: Vec, } /// Merge backup sources @@ -121,8 +120,8 @@ pub struct BackupCmd { /// * `left` - Vector of backup sources pub(crate) fn merge_sources(left: &mut Vec, mut right: Vec) { left.append(&mut right); - left.sort_by(|opt1, opt2| opt1.source.cmp(&opt2.source)); - left.dedup_by(|opt1, opt2| opt1.source == opt2.source); + left.sort_by(|opt1, opt2| opt1.sources.cmp(&opt2.sources)); + left.dedup_by(|opt1, opt2| opt1.sources == opt2.sources); } impl Runnable for BackupCmd { @@ -153,26 +152,26 @@ impl BackupCmd { .to_indexed_ids()?; // manually check for a "source" field, check is not done by serde, see above. - if !config.backup.source.is_empty() { + if !config.backup.sources.is_empty() { bail!("key \"source\" is not valid in the [backup] section!"); } - let config_opts = &config.backup.sources; + let snapshot_opts = &config.backup.snapshots; // manually check for a "sources" field, check is not done by serde, see above. - if config_opts.iter().any(|opt| !opt.sources.is_empty()) { + if snapshot_opts.iter().any(|opt| !opt.snapshots.is_empty()) { bail!("key \"sources\" is not valid in a [[backup.sources]] section!"); } - let config_sources: Vec<_> = config_opts + let config_snapshot_sources: Vec<_> = snapshot_opts .iter() .map(|opt| -> Result<_> { - Ok(PathList::from_iter(&opt.source) + Ok(PathList::from_iter(&opt.sources) .sanitize() .with_context(|| { format!( "error sanitizing source=\"{:?}\" in config file", - opt.source + opt.sources ) })? .merge()) @@ -186,38 +185,41 @@ impl BackupCmd { }) .collect(); - let sources = match (self.cli_sources.is_empty(), config_opts.is_empty()) { + let snapshot_sources = match (self.cli_sources.is_empty(), snapshot_opts.is_empty()) { (false, _) => { let item = PathList::from_iter(&self.cli_sources).sanitize()?; vec![item] } (true, false) => { info!("using all backup sources from config file."); - config_sources.clone() + config_snapshot_sources.clone() } (true, true) => { bail!("no backup source given."); } }; - for source in sources { + for sources in snapshot_sources { let mut opts = self.clone(); // merge Options from config file, if given - if let Some(idx) = config_sources.iter().position(|s| s == &source) { - info!("merging source={source} section from config file"); - opts.merge(config_opts[idx].clone()); + if let Some(idx) = config_snapshot_sources.iter().position(|s| s == &sources) { + info!("merging source={sources} section from config file"); + opts.merge(snapshot_opts[idx].clone()); } if let Some(path) = &opts.as_path { // as_path only works in combination with a single target - if source.len() > 1 { + if sources.len() > 1 { bail!("as-path only works with a single target!"); } // merge Options from config file using as_path, if given if let Some(path) = path.as_os_str().to_str() { - if let Some(idx) = config_opts.iter().position(|opt| opt.source == vec![path]) { + if let Some(idx) = snapshot_opts + .iter() + .position(|opt| opt.sources == vec![path]) + { info!("merging source=\"{path}\" section from config file"); - opts.merge(config_opts[idx].clone()); + opts.merge(snapshot_opts[idx].clone()); } } } @@ -233,7 +235,7 @@ impl BackupCmd { .ignore_filter_opts(opts.ignore_filter_opts) .no_scan(opts.no_scan) .dry_run(config.global.dry_run); - let snap = repo.backup(&backup_opts, &source, opts.snap_opts.to_snapshot()?)?; + let snap = repo.backup(&backup_opts, &sources, opts.snap_opts.to_snapshot()?)?; if opts.json { let mut stdout = std::io::stdout(); @@ -264,7 +266,7 @@ impl BackupCmd { println!("snapshot {} successfully saved.", snap.id); } - info!("backup of {source} done."); + info!("backup of {sources} done."); } Ok(()) diff --git a/tests/show-config-fixtures/empty.txt b/tests/show-config-fixtures/empty.txt index eacbf72e3..62ff899b0 100644 --- a/tests/show-config-fixtures/empty.txt +++ b/tests/show-config-fixtures/empty.txt @@ -1,5 +1,5 @@ [global] -use-profile = [] +use-profiles = [] dry-run = false check-index = false no-progress = false @@ -47,8 +47,8 @@ exclude-if-present = [] one-file-system = false tag = [] delete-never = false +snapshots = [] sources = [] -source = [] [copy] targets = []